Spring框架学习的第三天

作者: 清枫_小天 | 来源:发表于2016-07-16 14:53 被阅读459次

    代理模式

    代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

    1. 注意:
      委托类对象就是我们后面说到的 目标对象(需要【被】代理的对象)
      代理类对象就是我们后面说到的 代理对象(目标对象就是需要这个对象做为代理)
    2. 按照代理类的创建时期,代理类可分为两种。
      静态代理类:
      由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
      动态代理类:在程序运行时,运用反射机制动态创建而成。

    静态代理

    例如:
    接口:HelloService
    委托类:HelloServiceImpl
    代理类:HelloServiceProxy

    public interface HelloService{
      public String echo(String msg);
      public Date getTime();
    }
    
    public class HelloServiceImpl implements HelloService{
      public String echo(String msg){
        return "echo:"+msg;
      }
      public Date getTime(){
       return new Date();
      }
    }
    
    public class HelloServiceProxy implements HelloService{
      private HelloService helloService; //表示被代理的HelloService 实例
      public HelloServiceProxy(HelloService helloService){
        this.helloService=helloService;
      }
      public void setHelloServiceProxy(HelloService helloService){
         this.helloService=helloService;
      }
      public String echo(String msg){
        System.out.println("before calling echo()"); //目标方法调前处理
        //调用委托类对象的方法(也就是目标对象方法/被代理对象方法)
        //这个方法才是我们真正要执行的方法
        String result=helloService.echo(msg); 
        System.out.println("after calling echo()"); //目标方法调用后处理
        return result;
      }
      public Date getTime(){
        System.out.println("before calling getTime()"); //目标方法调前处理
    
        //调用委托类对象的方法(也就是目标对象方法/被代理对象方法)
        //这个方法才是我们真正要执行的方法
        Date date=helloService.getTime();
    
        System.out.println("after calling getTime()"); //目标方法调用后处理
        return date;
       }
    }
    

    main:

       HelloService helloService=new HelloServiceImpl();
       HelloService helloServiceProxy=new HelloServiceProxy(helloService);
       System.out.println(helloServiceProxy.echo("hello"));
    

    动态代理

    与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。 java.lang.reflect 包下面的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
    例子:
    接口:

    public interface IStudentService {
    
    void save(Student s);
    
    void delete(long id);
    
    Student find(long id);
    
     }
    

    日志类:

    public class StudentLogger {
    
    public void log(String msg){
        System.out.println("log: "+msg);
    }
    
    }
    

    实现类

    public class StudentServiceImpl implements IStudentService {
    public void delete(long id) {
        // 记录日志
        System.out.println("student is deleted...");
    }
    
    public Student find(long id) {
        // 记录日志
        System.out.println("student is found...");
        return null;
    }
    
    public void save(Student s) {
        // 记录日志
        System.out.println("student is saved...");
    }
    }
    

    //InvocationHandler接口的实现类,java的动态代理中需要使用

    public class MyHandler implements InvocationHandler {
    //目标对象
    private Object target; 
    private StudentLogger logger = new StudentLogger();
    
    public MyHandler() {
    }
    
    public MyHandler(Object target) {
        this.target = target;
    }
    
    // 参数1 将来所产生的代理对象 Proxy4$
    // 参数2 将来需要调用到的目标对象里面真正的那个方法的镜像
    // 参数3 将来调用方法的时候所传的参数
    public Object invoke(Object proxy, Method m, Object[] args)
            throws Throwable {
        // 获得将来所调用方法的名字
        String methodName = m.getName();
        // 用日志记录输出一下
        logger.log(methodName + " is invoked...");
        // 用反射的方式去调用将来需要真正调用的方法.
        Object o = m.invoke(target, args);
    
        return o;
    }
    get/set
    ....
     }
    

    main:

    //目标对象
    IStudentService service = new StudentServiceImpl();
    //service是我们的目标对象。
    //我们要给目标对象产生代理对象。
    //目标对象service只能单独执行delete方法。
    //但是我们需要的是:先执行log日志方法再执行delete方法。
    //目标对象service做不到这个要求,所以我们要给目标对象service
    //生成一个代理对象去完成这俩个操作.
    
    //怎么给目标对象生成代理对象:
    //JDK动态代理的方式
    
    //获得目标对象的Class对象
    Class c = service.getClass();
    //获得目标对象的类加载器对象
    ClassLoader classLoader = c.getClassLoader();
    
    //获得目标对象所实现的所有接口
    Class[] interfaces = c.getInterfaces();
    
    //获得一个InvocationHandler接口的实现类对象,并把目标对象传进去
    InvocationHandler h = 
            new MyHandler(service);
    
    //参数1 目标对象的类加载器对象
    //参数2 目标对象所实现的所有接口. Class类型数组
    //参数3 InvocationHandler接口的实现类对象
    IStudentService proxy = 
        (IStudentService)Proxy.newProxyInstance
        (classLoader, interfaces, h);
    //这里的proxy是一个实现了IStudentService接口动态生成的代理类的对象
    proxy.delete();
    

    CGLib代理

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为目标对象创建一个子类对象,并在子类对象中拦截所有父类方法的调用,然后在方法调用前后调用后都可以加入自己想要执行的代码。JDK动态代理与CGLib动态代理都是Spring AOP的采用的代理方式。

    简单的实现

    这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理。

    public class SayHello {
     public void say(){
         System.out.println("hello everyone");
     }
    }
    

    注意:在cglib方式中,目标对象作为父类,代理对象作为目标对象动态生成的子类对象

    1. 该类实现了创建一个类的子类的方法(cglib给一个类生成代理对象的方式)
    2. getProxy(SuperClass.class)方法通过参数即父类的class对象,创建出它的一个子类对象,也就是cglib方式的代理对象
    3. intercept()方法拦截所有目标类方法的调用,
    4. obj表示将来生成的代理对象,
    5. method为目标类中方法的反射对象,args为方法的动态入参,
    6. mproxy为代理类(子类)中方法的反射对象。
    7. mproxy.invokeSuper(obj, args)通过代理类调用目标对象(父类)中的方法。
        public class CglibProxy implements MethodInterceptor{
    
         public Object getProxy(Class clazz){
         Enhancer enhancer = new Enhancer();
         //设置谁是父类
         enhancer.setSuperclass(clazz);
         enhancer.setCallback(this);
         //通过字节码技术动态创建子类实例
         return enhancer.create();
         }
    

    //实现MethodInterceptor接口方法

     public Object intercept(Object obj, Method method, Object[] args,
         MethodProxy mproxy) throws Throwable {
         System.out.println("前置代理");
         //通过代理类调用父类中的方法
         Object result = mproxy.invokeSuper(obj, args);
         System.out.println("后置代理");
         return result;
      }
     }
    

    main:

       CglibProxy proxy = new CglibProxy();
       //通过生成子类的方式创建代理类
       SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
       proxyImp.say();
    

    输出结果:
    前置代理
    hello everyone
    后置代理

    Spring实现AOP(Aspect Oriented Programming)是依赖JDK动态代理和CGLIB代理(不同情况spring会自己选择一种方式)。

    JDK动态代理和CGLIB代理的对比:
    JDK动态代理
    其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
    CGLIB代理
    实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的。

    所以spring会有以下俩种选择动态代理实现方式的情况:

    • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
    • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间自动选择;

    认识AOP中的一些基本概念,然后在一个一个的例子中,不断的加强对这些概念的理解同时要能自己表述出每个概念的含义

    AOP          面向切面编程
    
    aspect        切面/切面类
    
    joinPoint    连接点
            在spring的aop中只有 类中的方法 可以做连接点,每一个方法都可以是一个连接点.
        
    pointCut    切入点 
            一组连接点的集合
    
    advice        通知/拦截器 
            用来控制切面类将来到底是织入到切入点的前面、后面或者是抛异常的时候。
    
    adivsor        增强器
            用来筛选类中的哪些方法是我们的连接点(哪些方法需要被拦截).
    
    target        目标对象
    
    proxy        代理对象
    
    wave        织入
    
    1. 前置通知(Before advice):
      在某连接点(join point)之前执行的通知

    2. 返回后通知(After returning advice):
      在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

    3. 抛出异常后通知(After throwing advice):
      在方法抛出异常退出时执行的通知。

    4. 后通知(After (finally) advice):
      当某连接点退出的时候执行的通知

    5. 环绕通知(Around Advice):
      包围一个连接点(join point)的通知,例如事务的处理,就需要这样的通知,因为事务需要在方法前开启,在方法后提交

    在Spring中,Advice是由spring中的几个接口来指定(就像action类由struts2中的action接口来指定一样),主要有以下几种:

    Before Advice

        public interface MethodBeforeAdvice extends BeforeAdvice {
        void before(Method m, Object[] args, Object target) throws Throwable;
         }
    

    例如:
    //有一下几个类或者接口:
    Account.java

        private int id;
        private String name;
        private double balance;//余额
        get/set
        ...
    

    AccountDao.java

        //取款 账号减去多少钱
        void withdraw(Account acc,double amt);
        //存款 账号加上多少钱
        void deposit(Account acc,double amt);
    

    AccountDaoImpl.java

        //简单的实现接口中的抽象方式
    

    IAccountService.java

        //银行账号的一个操作:例如转账
        void bankAction();
    

    AccountServiceImpl.java

        private AccountDao accountDao;
        private Account account;
        //转账
        public void bankAction(){
            accountDao.withdraw(account, 100);
            accountDao.deposit(account, 100);
        }
        get/set
        ...
    

    //切面类

    public class MyLogger {
        public void log(String msg){
            System.out.println("log:"+msg);
        }
     }
    

    我们要做的事情:在转账方法(bankAction)执行之前进行一个日志输出

    //前置通知:作用Spring会在目标方法执行之前调用这个before方法

    public class BeforeAdvice implements MethodBeforeAdvice {
        //切面类
        private MyLogger logger;
    
        // 参数1 将来我们需要调用的目标对象中的方法镜像
        // 参数2 将来调用方法的时候所传过来的参数
        // 参数3 目标对象
       //将来在调用目标对象方法之前,会先执行这个before方法
        //在此方法只需要去写,代理对象要新增的功能呢=,不需要手动调用目标对象
        //所执行的方法。
        public void before(Method m, Object[] args, Object target) throws Throwable {
    
            logger.log(m.getName() + " is invoked..");
    
            /*
             * 注意:这里一定不要自己手动的用反射去 调用这个目标对象中的方法,
             * 因为spring 会帮我们去调用的,如果我们这个再去调用这个方法,
             * 那么这这个方法会被调用俩次.
             * 
             * m.invoke(target,args);
             * 
             */
        }
        get/set
    }
    

    配置xml文件: 注意ProxyFactoryBean的配置,htmlsingle中搜索即可

        <!-- 配置切面类 -->
        <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
        
        <!-- 配置advice -->
        <bean name="beforeAdvice" 
        class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
        </bean>
        
        <!-- 配置dao层对象 -->
        <bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
        
        <!-- 配置目标对象 -->
        <bean name="target" 
        class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
        </bean>
        
        <!-- 配置代理对象 -->
        <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
        <bean name="proxy" 
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
        
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
        
        <!-- 注入advice  可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>
            </list>
        </property>
        
        </bean>
    

    After advice

    public interface AfterReturningAdvice extends Advice {
        void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
    }
    

    例如:

    public class AfterAdvice implements AfterReturningAdvice {
        private MyLogger logger;
        
        //参数1 目标对象中的方法执行完返回值
        //参数2 所执行方法的镜像对象
        //参数3 执行方法时候所传的参数
        //参数4 目标对象
        //将来调用目标对象的方法之后会执行这个afterReturning方法
        public void afterReturning(Object returnValue, Method method,
                Object[] args, Object target) throws Throwable {
            
            logger.log("after returning " + " target=" + target
                    + " method Name=" + method.getName() + " args are:" + args
                    + " returnValue=" + returnValue);
        }
        get/set
    }
    

    xml配置文件:

    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="afterAdvice"
        class="com.briup.aop.after.AfterAdvice">
        <property name="logger" ref="logger"></property>    
    </bean>
    
    <!-- 配置dao层对象 -->
    <bean id="dao" class="com.briup.aop.dao.AccountDaoImpl" />
    
    <!-- 配置目标对象 -->
    <bean name="target"
        class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <!-- 配置代理对象 -->
    <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
    <bean name="proxy"
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
    
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
    
        <!-- 注入advice  可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>afterAdvice</value>
            </list>
        </property>
    
    </bean>
    

    注意:另外一个返回后通知接口:AfterReturningAdvice的使用方式和这个是类似的,但是需要注意它们俩个之间的区别

    环绕Advice:

    public interface MethodInterceptor extends Interceptor {
          Object invoke(MethodInvocation invocation) throws Throwable;
    }
    

    例如:

    public class AroundAdvice implements MethodInterceptor {
    private MyLogger logger;
    
    public Object invoke(MethodInvocation mi) throws Throwable {
        // mi.getMethod()获得将来要调用的方法的镜像
        //在目标方法执行之前做日志
        logger.log(mi.getMethod().getName() + " is start...");
        
        // 这个方法就是用来调用目标对象中的方法的
        Object returnValue = mi.proceed();
        
        //在目标方法执行之后做日志
        logger.log(mi.getMethod().getName() + " is end...");
    
        return returnValue;
    }
    get/set
    

    xml配置文件:

        <!-- 配置切面类 -->
        <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
        
        <!-- 配置advice -->
        <bean name="aroundAdvice" 
        class="com.briup.aop.around.AroundAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
        </bean>
        
        <!-- 配置dao层对象 -->
        <bean name="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
         
        <!-- 配置目标对象 -->
        <bean name="target" 
        class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
        </bean>
        
        <!-- 配置代理对象 -->
        <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
        <bean name="proxy" 
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
        
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
        
        <!-- 注入advice 可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>aroundAdvice</value>
            </list>
        </property>
        </bean>
    

    Throws Advice

    //ThrowsAdvice 是一个空接口,起标识作用

    public interface ThrowsAdvice extends Advice {
    
    }
    

    例如:

    public class ThrowingAdvice implements ThrowsAdvice {
        private MyLogger logger;
    
        public MyLogger getLogger() {
            return logger;
        }
    
        public void setLogger(MyLogger logger) {
            this.logger = logger;
        }
    
        //这里这个方法的名字一定要叫afterThrowing
        //参数可以是1个也可以是四个
        //1个参数的时候只能是一个异常类型的参数
        //如果是4个参数的话,参数的顺序也一定要是下面的顺序
        public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
            logger.log(e.getMessage());
        }
        
        //下面这样写也可以
        /*
        public void afterThrowing(Exception e) {
            
            logger.log(e.getMessage());
        }
        */
        get/set
    }
    

    配置xml文件:

        <!-- 配置切面类 -->
        <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
        
        <!-- 配置advice -->
        <bean name="throwAdvice" class="com.briup.aop.throwException.ThrowingAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
        </bean>
        
        <!-- 配置dao层对象 -->
        <bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
        
        <!-- 配置目标对象 -->
        <bean name="target" 
        class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
        </bean>
        
        <!-- 配置代理对象 -->
        <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
        <bean name="proxy" 
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
        
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
        
        <!-- 注入advice  可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>throwAdvice</value>
            </list>
        </property>
        </bean>
    

    advisor

    作用:筛选要拦截(要代理)的方法,之前的advice是把目标对象中的所有方法全部都进行代理
    指定为advisor的接口为:

    public interface PointcutAdvisor {
        Pointcut getPointcut();
        Advice getAdvice();
    } 
    

    spring中已经给我们提供了实现类RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了

    xml配置文件:

    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>

    <!-- 配置advice -->
    <bean name="beforeAdvice" 
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    <!-- 配置advisor 增强器-->
    <!-- 作用:筛选要拦截(要代理)的方法 -->
    <bean name="advisor" 
    class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice 表示增强器要在哪一个advice起作用-->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法(连接点) -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao" 
     class="com.briup.aop.dao.AccountDaoImpl"/>
    <!-- 配置目标对象 -->
    <bean name="target" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <!-- 配置代理对象 -->
    <!-- 这里使用的是spring的一个代理对象工厂类产生的 -->
    <bean name="proxy" 
    class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 注入目标对象 -->
        <property name="target" ref="target"></property>
        
        <!-- 注入目标对象所实现的接口 可以有多个接口 -->
        <property name="proxyInterfaces">
            <list>
                <value>com.briup.aop.service.IAccountService</value>
            </list>
        </property>
        
        <!-- 注入advice/advisor  可以有多个 -->
        <property name="interceptorNames">
            <list>
                <value>advisor</value>
            </list>
        </property>
    </bean>
    

    AutoProxy 自动代理:DefaultAdvisorAutoProxyCreator类的使用

    使用原因:在配置文件中我们往往需要给很多个目标对象设置代理对象,那么上面例子的方式就需要每个目标对象的代理对象都需要配置一套类似的标签
    自动代理:可以用很少的配置为xml文件中的目标对象自动的生成对应的代理对象

    xml配置文件:

    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="beforeAdvice" 
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    <!-- 配置advisor -->
    <!-- 作用:筛选要拦截的方法 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法 -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
                <value>.*deposit</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
     
    <!-- 配置目标对象 -->
    <bean name="target" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target2" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target3" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    
    <!-- 配置代理对象 -->
    <!-- 这里使用自动代理的方式 autoproxy -->
    <!-- 注意:这不是一个工厂类,所以不能用过proxy来拿代理对象 -->
    <bean name="proxy" 
    class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    </bean>
    

    使用自动代理的时候需要注意的方面:

    1. 当前的配置里面一定要有一个advisor的配置
    2. 不需要向自动代理类中注入任何信息
    3. 不管目标对象是否实现了一个或多接口,自动代理的方式都能够为它产生代理对象(CGLib的方式).
    4. 从spring容器中拿代理对象的时候,需要通过目标对象的名字来拿。

    AutoProxyByName 通过名字进行自动代理:BeanNameAutoProxyCreator类的使用

    使用原因:虽然自动代理可以很方便的给xml文件中的目标对象设置对应的代理对象,但是并不是xml文件中的所有对象都是我们的目标对象,我们更想希望可以进一步筛选出某几个对象为我们的目标对象
    名字进行自动代理:解决了上面的问题,给我们提供了筛选目标对象的配置方式
    

    xml配置文件:

    <!-- 配置切面类 -->
    <bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
    
    <!-- 配置advice -->
    <bean name="beforeAdvice" 
    class="com.briup.aop.before.BeforeAdvice">
        <!-- 注入切面类对象 -->
        <property name="logger" ref="logger"></property>
    </bean>
    
    
    <!-- 配置advisor -->
    <!-- 作用:筛选要拦截的方法 -->
    <bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 注入advice -->
        <property name="advice" ref="beforeAdvice"></property>
        <!-- 注入需要被拦截的目标对象中的方法 -->
        <property name="patterns">
            <list>
                <value>.*bankAction</value>
                <value>.*deposit</value>
                <value>.*withdraw</value>
            </list>
        </property>
    </bean>
    
    <bean id="dao" 
     class="com.briup.aop.dao.AccountDaoImpl"/>
     
    <!-- 配置目标对象 -->
    <bean name="target" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target2" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    <bean name="target3" 
    class="com.briup.aop.service.AccountServiceImpl">
        <property name="accountDao" ref="dao"></property>
    </bean>
    
    
    
    <!-- 配置代理对象 -->
    <!-- 这里使用自动代理的方式 autoproxybyname -->
    <bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!-- 注入需要被代理的对象名字 -->
           <property name="beanNames">
               <list>
                   <value>target</value>
                   <value>target2</value>
                   <value>dao</value>
               </list>
           </property>
           
           <!-- 注入advice或者advisor -->
           <property name="interceptorNames">
               <list>
                   <value>advisor</value>
               </list>
           </property>
    </bean>
    

    使用自动代理的时候需要注意的方面:

    1. 当前的配置里面有没有advisor的配置都没关系
    2. 需要向自动代理类中注入被代理目标对象的名字已经advice或者advisor
    3. 不管目标对象是否实现了一个或多接口,自动代理的方式
      都能够为它产生代理对象.
    4. 从spring容器中拿代理对象的时候,需要通过目标对象的
      名字来拿。

    Spring框架的学习第一天
    Spring框架的学习第二天

    相关文章

      网友评论

        本文标题:Spring框架学习的第三天

        本文链接:https://www.haomeiwen.com/subject/xdrujttx.html