spring笔记

101 阅读5分钟

1.IOC全注解(annocation)

1.1 在配置文件中告诉spring去哪里扫描实体对象

如果要使用ioc注解,一定要配置一个<context:component-scan base-package=""> ,让spring去扫描包及其子包里面的类,如果配置多个包包之间用逗号隔开

<context:component-scan base-package="com.jrsoft.modules,com.jrsoft.engine">
   <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

1.2在类型上面添加anntation

  1. @Component:一般写在工具类上面
  2. @Service:一般写在服务对象上
  3. @Repository:一般写在Dao上面
  4. @Controller:一般写在Action上,并且在springMVC中,Controller就在标示一个Controller对象 这四个标签对于spring来说作用相同,主要是给程序员看的

1.3具体使用

xml配置中完整bean配置如下

<bean id="" class="" factory-method="" init-method="" destroy-method="" scope=""/> 

在对应注解中的使用

  1. id:所有四个标签中都支持value值,这个值就是id的名字
  2. class:不用写了
  3. factory-method和factory-bean没有对应的标签,如果有这种需要,直接配置到xml中(annocation和xml可以混合使用)
  4. init-method:使用@PostConstruct直接放在init方法上。(注意initMethod一定是在DI之后执行的)
  5. destory-method:使用@PreDestroy直接放在destroy方法上
  6. scope:在类上面添加@Scope("prototype") 示例
@Component
@Scope("prototype")
public class JdbcUtil {
    @PostConstruct
    public void init(){
        System.out.println("初始化DataSource");
    }
    @PreDestroy
    public void close(){
        System.out.println("DataSource关闭");
    }

}

2.静态代理

2.1问题分析

传统session操作中事务的开启和关闭在dao层,当一条业务需要多个dao来完成是,这样会造成无法控制事务问题 解决办法

  1. sessionFactory.getCurrentSession得到当前线程中的session
  2. 把session的开启、事务的开启和事务的提交从dao中移到service方法中 (我们应用中,事务都应该开在service的方法上面)

事务直接开在service中存在的问题

存在一个事务跨层问题,如果直接把事务管理的代码写在service上会造成service代码污染,最好的方式就是将事务的代码从service中抽出来,使用代理模式再加到目标代码中

2.2代码实现

目标对象

public class EmployeeServiceImpl implements EmployeeService{
    @Override
    public void save() {
        System.out.println("dao.save()");
    }
    @Override
    public void update() {
        System.out.println("dao.update()");
    }
}

代理对象

public class EmployeeTranscationServiceImpl implements EmployeeService{

    private EmployeeService target;

    public EmployeeTranscationServiceImpl(EmployeeService target) {
        this.target = target;
    }

    @Override
    public void save() {
        System.out.println("session.getCurrentSession");
        System.out.println("session.getTranscation().begin()");
        target.save();
        System.out.println("session.getTranscation().commit()");
    }

    @Override
    public void update() {
        System.out.println("session.getCurrentSession");
        System.out.println("session.getTranscation().begin()");
        target.save();
        System.out.println("session.getTranscation().commit()");
    }
}

配置

<bean id="target" class="com.jrsoft.gdb.test.EmployeeServiceImpl" />
<bean id="EmployeeService" class="com.jrsoft.gdb.test.EmployeeTranscationServiceImpl">
   <constructor-arg ref="target" />
</bean>

测试

@RunWith(SpringJUnit4ClassRunner.class)
public class StaticProxyTest {

    @Autowired
    @Qualifier("employeeService")
    private EmployeeService service;

    @Test
    public void testSave(){
        service.save();
    }

}

静态代理分析

静态代理确实出来了代码污染问题,但仍然存在以下两个问题:

  1. 重复的代码仍然分散在各个方法中
  2. 需要为每一个真实对象写一个代理对象

3.动态代理

例子:拦截器

3.1 有接口对象的动态代理

  1. 客户端调用save方法,执行被InvocationHanalder拦截
  2. 在invoke方法中执行添加的额外的逻辑
  3. 调用m.invoke(target,args)执行到了真实的目标对象上对应的方法
  4. 真实对象返回执行结果到InvocationHanalder里面
  5. 执行完其他逻辑,把结果返回给客户端 相同的ClassLoader加载的类才能相互访问

JDK中提供了针对有接口对象的动态代理实现方式

public class TranscationInvocationHandler implements InvocationHandler {

    private IEmployeeService target;
    private TranscationManager txManager;

    public TranscationInvocationHandler(IEmployeeService target, TranscationManager txManager) {
        this.target = target;
        this.txManager = txManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        txManager.begin();
        try{
            Object ret = method.invoke(target,args);
            txManager.commit();
            return ret;
        }catch (Exception e){
            txManager.rollback();
        }
        return null;
    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DynProxyTest {

    @Autowired
    private IEmployeeService service;

    @Autowired
    private TranscationManager txManager;

    @Test
    public void testSave(){
        IEmployeeService o = (IEmployeeService)Proxy.newProxyInstance(service.getClass().getClassLoader(),
                new Class[] {IEmployeeService.class},new TranscationInvocationHandler(service,txManager));
        o.update();
    }

}

3.2 没有接口的对象的动态代理(使用继承)

public class LogInvocationHandler implements InvocationHandler {

    private Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("您当前调用了"+method);
        
        return method.invoke(target,args);
    }
}

测试

public class GclibTest {

    @Test
    public void testSave(){
        SomeBean target = new SomeBean();
        //增强器
        Enhancer enhancer = new Enhancer();
        //设置目标对象的classLoader
        enhancer.setClassLoader(target.getClass().getClassLoader());
        //设置这个动态代理类的父类
        enhancer.setSuperclass(SomeBean.class);
        //设置要传入的拦截器
        enhancer.setCallback(new LogInvocationHandler(target));
        //使用cleate方法创建代理对象
        SomeBean o = (SomeBean)enhancer.create();
        o.prinnt1();
    }

}

4.Spring AOP

AOP是一种更高级的动态代理的使用,中文解释为:面向切面编程(Aspect Oritention Programing)

4.1 AOP中的重要概念

  • 切入点:要加入业务逻辑的点(在哪些类的哪些方法上)
  • 通知:通知包含两个方面,1:代表方法的执行时间,2:在这个时间上面要做什么事情
  • 切面:一个切入点+一个通知(在什么地方,什么时候,做什么事情)
  • 织入:把一个切面应用到真实对象上面的过程就叫织入 在java中,没有语言能够准确的描述切入点,所以有一个AspectJ,这是一种提供了描述切入点的语言

4.2 Spring中AOP的使用方式

1.使用xml配置

<aop:config>
    <!--配置一个切入点,expression:切入点的表达式,id:切入点的名称-->
    <aop:pointcut id="po" expression="execution(* com.jrsoft..*Impl.*(..)) || execution(* com.jrsoft.business..*Impl.*(..))"/>
     <!--配置一个切面,代表在这个切面的定义中,所有的做什么事情,都是有txManager这个对象的方法提供的-->
    <aop:aspect ref="txManager">
        <!--before代表在方法执行之前-->
        <aop:before method="begin" pointcut-ref="po"/>
        <!--after-returning代表方法正常执行之后-->
        <aop:after-returning method="commit" pointcut-ref="po"/>
        <!--after-throwing代表在方法抛出异常的时候-->
        <aop:after-throwing method="begin" pointcut-ref="po"/>
    </aop:aspect>
</aop:config>

切入点表达式

image.png 没有实现接口对象的配置

<bean id="myservice" class="org..MyService"/>
<aop:config>
    <!--配置一个切入点,expression:切入点的表达式,id:切入点的名称-->
    <aop:pointcut id="po" expression="execution(* com.jrsoft..*Impl.*(..)) || execution(* com.jrsoft.business..*Impl.*(..))"/>
     <!--配置一个切面,代表在这个切面的定义中,所有的做什么事情,都是有txManager这个对象的方法提供的-->
    <aop:aspect ref="myservice">
        <!--before代表在方法执行之前-->
        <aop:before method="begin" pointcut-ref="po"/>
        <!--after-returning代表方法正常执行之后-->
        <aop:after-returning method="commit" pointcut-ref="po"/>
        <!--after-throwing代表在方法抛出异常的时候-->
        <aop:after-throwing method="begin" pointcut-ref="po" throwing="ex"/>
    </aop:aspect>
</aop:config>
public class MyService{

    /**
     * 在方法正常执行之后
     */
    public void afterReturning() {

    }

    /**
     * 在方法执行之后,在finally里面
     */
    public void update() {

    }

    /**
     * 在抛出异常的时候,如果要得到抛出的异常,1:在方法中添加一个参数,起一个参数名字,2:在<aop:after-throwing中添加一个throwing属性,
     * 属性的名字就是这个方法的参数的名字
     */
    public void afterThrowing() {

    }

    public Object around(ProceedingJoinPoint pjp) {
        try {
            System.out.println("before");
            Object ret = pjp.proceed();
            System.out.println("after returning");
        }catch (Throwable ex){
            System.out.println("after throwing");
        }finally {
            System.out.println(after);
        }
        return null;
    }
}

小结

  1. 准备一个真实的对象,准备一个类,这个类里面的方法用来提供通知里面做什么
  2. 这两个类都要配置到spring容器中
  3. 配置springAOP
引入AOP命名空间
<aop:config>
    <aop:pointcut id="" expression=""/>
    <aop:aspect ref="">
        <aop:before method="" pointcut-ref=""/>
        <aop:after-returning method="" pointcut-ref=""/>
        <aop:after-throwing method="" pointcut-ref=""/>
    </aop:aspect>
</aop:config>
  1. 我们在客户端还是注入的是spring里面配置的真实对象,实际上spring在为这些对象创建好代理对象之后,会使用这些创建好的代理对象去替换掉容器中的原始对象

SpringAOP执行流程

  1. 解析xml
  2. 实例化所有bean
  3. 解析aop:config

Spring中使用标签完成AOP

要使用annotation的AOP,需要在xml中引入aspectj-autoproxy,这个元素相当于引入了用于解析@Aspect和@Before等元素的第三方程序。

<aop:aspectj-autoproxy/>

应用示例

/**
 * @Aspect标签相当于 <aop:aspect ref=""/>
 */
@Aspect
@Component
public class EmployeeServiceImpl{


    /**声明一个pointcut
     * <aop:pointcut id="pc" expression="execution(* com.jrsoft..*Impl.*(..))"/>
     */
    @Pointcut("execution(* com.jrsoft..*Impl.*(..))")
    public void pc(){
    }

    /**
     * <aop:before method="before"/>
     * 1.可以直接写point-cut表达式
     * 2.使用定义好的pointout
     */
    @Before("pc()")
    public void before(){
    }

    /**
     * 在方法正常执行之后
     */
    @AfterReturning("pc()")
    public void afterReturning(){
    }
    @After("pc()")
    public void after(){
    }

    /**
     * 在抛出异常的时候,如果要得到抛出的异常,1:在方法中添加一个参数,起一个参数名字,2:在<aop:after-throwing中
     * 添加一个throwing属性,属性的名字就是这个方法的参数名字
     * @param ex
     */
    @AfterThrowing(pointcut = "pc()",throwing = "ex")
    public void afterThrowing(Throwable ex){
    }
}

spring中AOP的通知类型:

  • aop:before 前置通知
  • aop:after-returning 后置通知
  • aop:after 最终通知
  • aop:after-throwing 异常通知
  • aop:around 环绕通知