zoukankan      html  css  js  c++  java
  • Spring学习记录(二)

    1.Spring中的AOP思想

    aop思想:横向重复,纵向抽取。

    AOP(Aspect-OrientedProgramming,面向切面编程),AOP包括切面(Aspect),通知(Advice),连接点(joinpoint),

    实现方式就是通过目标对象的代理在连接点前后加入通知,完成统一的切面操作。

    这个思想在filter过滤器,拦截器,动态代理中就已经使用过,例如:

    (1) 在解决乱码的问题上

    (2) 在动态代理上,管理事务:(Proxy.newProxyInstance(Classloader cloader,Interface[] arr, InvocationHandle h))

    (3) 拦截器中在参数赋值上,一般每个action的参数赋值,都需要有这个参数,但是有拦截器可以完成把参数赋值到action中,或者校验登录

     2.AOP实现原理

    Spring能够为容器中管理的对象生成动态代理对象。

    以前未使用Spring时,要使用动态代理,我们需要自己调用这个方法:

    Proxy.newProxyInstance(Classloader cloader,Interface[] arr, InvocationHandle h),生成代理对象。

    使用Spring后,能帮助我们生成代理对象(只需要在配置文件中配置,或者注解配置即可),那么为什么与AOP思想有关呢?

    比如上面例子中的管理事务,使用动态代理对象在进行事务的管理,只需要写一次代码就能够完成,但是需要自己手写代理对象,而用Spring可以自动的生成Service的代理对象,所以称为Spring的AOP思想

    所以根据上面的铺垫,AOP的实现原理可以说就是动态代理,或者cglib代理,还有静态织入(引入特定的语法创建“方面”,从而是的编译器可以在编译期间织入有关“方面”的代码)。 ()

    (1) 动态代理(优先):被代理对象必须要实现接口,才能产生代理对象,如果没有借口将不能使用动态代理技术

    (2) cglib代理(没有接口):第三方代理技术,cglib代理可以对任何类生成代理,代理的原理是对目标对象进行继承代理。如果目标对象被final修饰,那么该类无法被cglib代理。

    使用动态代理的一个代码示例:

    首先创建接口,以及实现类

    /**
     * Created by YuKai Fan on 2018/9/27.
     */
    public interface UserService {
        void save();
        void delete();
        void update();
        void find();
    }
    /**
     * Created by YuKai Fan on 2018/9/27.
     */
    public class UserServiceImpl implements UserService {
        //这些方法都是业务逻辑,一般来说在进行业务方法进行事务管理
        @Override
        public void save() {
            //如果不使用动态代理每个方法都需要进行打开事务,提交事务的操作
            //System.out.println("打开事务");
            System.out.println("保存用户");
            //System.out.println("提交事务");
        }
    
        @Override
        public void delete() {
            System.out.println("删除用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改用户");
        }
    
        @Override
        public void find() {
            System.out.println("查找用户");
        }
    }

    再创建一个代理工厂,来生成上面接口的一个代理对象

    /**
     * 用户service层的代理工厂
     * Created by YuKai Fan on 2018/9/27.
     */
    public class UserServiceProxyFactory implements InvocationHandler{
    
        private UserService us;
    
        public UserServiceProxyFactory(UserService us) {
            super();
            this.us = us;
        }
    
        public UserService getUserServiceProxy() {
            //生成动态代理
            UserService usProxy  = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
                    UserServiceImpl.class.getInterfaces(), this);//第三个参数,必须要实现InvocationHandler接口
    
            return usProxy;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //使用动态代理,只需要写一次即可
            System.out.println("打开事务");
            Object invoke = method.invoke(us, args);//方法的执行,需要方法所存在的对象,所以需要有实例。创建一个构造方法,来传递一个执行方法所对应的实例对象
            System.out.println("提交事务");
            return invoke;
        }
    }

    测试:

    /**
     * Created by YuKai Fan on 2018/9/27.
     */
    public class Demo {
        public static void main(String[] args) {
            UserService us = new UserServiceImpl();
            UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
            UserService usProxy = factory.getUserServiceProxy();
            usProxy.save();
        }
    }

    测试结果:代理对象每次在执行业务逻辑代码时,都需要执行事务的操作,因为在代理对象在执行方法时,先进行了事务操作。

     使用cglib代理的一个代码示例(了解):

    创建代理工厂

    /**
     * cglib代理(了解)
     * Created by YuKai Fan on 2018/9/27.
     */
    public class UserServiceProxyFactory2 implements MethodInterceptor{
    
        public UserService getUserServiceProxy() {
            //生成代理对象
            Enhancer en = new Enhancer();
            en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
            en.setCallback(this);//代理要做什么
            UserService us  = (UserService) en.create();//创建代理对象
            return us;
        }
    
        @Override
        //参数解析:methodProxy方法代理,method被代理的方法,o代理的原始对象,arg运行期的参数
        public Object intercept(Object o, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
            //打开事务
            System.out.println("打开事务");
            //调用原有方法
            Object returnValue = methodProxy.invokeSuper(o, arg);
            //提交事务
            System.out.println("提交事务");
            return returnValue;
        }
    }

    测试:

    /**
     * Created by YuKai Fan on 2018/9/27.
     */
    public class Demo {
        public static void main(String[] args) {
            UserService us = new UserServiceImpl();
            UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
            UserServiceProxyFactory2 factory2 = new UserServiceProxyFactory2();//cglib代理
            UserService usProxy = factory.getUserServiceProxy();
            UserService usProxy2 = factory2.getUserServiceProxy();//cglib生成的代理对象
            usProxy.save();
            usProxy2.delete();
    
            //判断代理对象是否输入被代理对象类型
            //只是代理对象与被代理对象实现了相同的接口,但是没有继承关系
            System.out.println(usProxy instanceof UserServiceImpl);//false
            //而cglib代理,是与被代理对象进行继承,所以类型相同
            System.out.println(usProxy2 instanceof UserServiceImpl);//true
        }
    }

    测试结果:

     3.AOP名词解析

    以上面的源代码为例

    1.Joinpoint(连接点):目标对象中,所有可以增强的方法。(联系上面的例子,就是指那些实现接口的实现类的所有业务方法,即save(),delete(),update(),find())

    2.Pointcut(切入点):目标对象,已经增强的方法。(创建代理对象后,进行增强的方法。比如在执行方法前开始事务,执行方法后提交事务。在上面的例子中,由于没有指定使用哪一个的特定方法,所以那四个方法全是切入点)

    3.Advice(通知/增强):增强的代码。(根据上面的例子,我们需要在执行目标方法前后,打开事务和提交事务,所以那些打开事务,提交事务的代码,就叫做通知)

    4.Target(目标对象):被代理对象(实现类,上面的UserServiceImpl)

    5.weaving(织入):将通知应用到切入点的过程(将打开事务和提交事务,应用到目标方法的过程)

    6.Proxy*代理):将通知应用到目标对象之后,形成代理对象

    7.aspect(切面):切入点+通知

    下面是Spring AOP的应用示例:

    步骤:

    (1) 导包

    (2) 准备目标对象

    /** 目标对象
     * Created by YuKai Fan on 2018/9/27.
     */
    public class UserServiceImpl implements UserService {
        //这些方法都是业务逻辑,一般来说在进行业务方法进行事务管理
        @Override
        public void save() {
            //如果不使用动态代理每个方法都需要进行打开事务,提交事务的操作
            //System.out.println("打开事务");
            System.out.println("保存用户");
            //System.out.println("提交事务");
        }
    
        @Override
        public void delete() {
            System.out.println("删除用户");
        }
    
        @Override
        public void update() {
            System.out.println("修改用户");
        }
    
        @Override
        public void find() {
            System.out.println("查找用户");
        }
    }

    (3) 准备通知

    /**
     * aop的通知类
     * Created by YuKai Fan on 2018/9/28.
     */
    public class MyAdvice {
        
        //前置通知:目标方法运行前调用
        //后置通知(如果出现异常不会调用):在目标方法之后调用
        //环绕通知:在目标方法之前和之后都调用
        //异常拦截通知:如果出现异常,就会调用
        //后置通知(无论是否出现异常都会通知):在目标方法之后调用
    
        //前置通知
        public void before() {
            System.out.println("这是前置通知!!");
        }
        //后置通知(如果出现异常不会调用)
        public void afterReturning() {
            System.out.println("这是后置通知!!(如果出现异常不会调用)");
        }
        //环绕通知,必须要手动的调用目标方法,所以需要有一个ProceedingJoinPoint对象
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println(
    "这是环绕通知之前!!"); Object proceed = pjp.proceed();//调用目标方法, System.out.println("这是环绕通知之后!!"); return proceed; } //异常通知 public void afterException() { System.out.println("出事了,出现异常了!!"); } //后置通知(无论是否出现异常都会通知) public void after() { System.out.println("这是后置通知(无论是否出现异常都会通知)!!"); } }

    (4) 配置进行织入,将通知应用到目标对象中

    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop
                               http://www.springframework.org/schema/aop/spring-aop-4.2.xsd  ">
    
       <!-- 准备工作:导入aop(约束)命名空间 -->
        <!-- 1.配置目标对象 -->
        <bean name="userService" class="com.javaweb.service.UserServiceImpl"></bean>
        <!-- 2.配置通知对象 -->
        <bean name="myAdvice" class="com.javaweb.aspect.MyAdvice"></bean>
        <!-- 配置将通知织入目标对象 -->
        <aop:config>
            <!-- 配置切入点
                    public void com.javaweb.service.UserServiceImpl.save(),只针对save方法进行织入
                    void com.javaweb.service.UserServiceImpl.save() public可以省略,默认就是public
                    * com.javaweb.service.UserServiceImpl.save() 对返回值不做要求
                    * com.javaweb.service.UserServiceImpl.*() 对目标类下的所有空参方法织入
                    * com.javaweb.service.UserServiceImpl.*(..) 对方法的参数不做任何要求
                    * com.javaweb.service.*ServiceImpl.*(..) 可以对任何在service包下以ServiceImpl结尾的类进行织入
                    * com.javaweb.service..*ServiceImpl.*(..) 可以对service下的子包也进行织入
            -->
            <aop:pointcut id="pc" expression="execution(* com.javaweb.service.*ServiceImpl.*(..))" />
            <aop:aspect ref="myAdvice">
                <!-- 指定名为before方法作为前置通知 -->
                <aop:before method="before" pointcut-ref="pc"/>
                <!-- 后置(无论是否出现异常) -->
                <aop:after method="after" pointcut-ref="pc"/>
                <!-- 环绕 -->
                <aop:around method="around" pointcut-ref="pc"/>
                <!-- 异常 -->
                <aop:after-throwing method="afterException" pointcut-ref="pc"/>
                <!-- 后置(出现异常不会调用) -->
                <aop:after-returning method="afterReturning" pointcut-ref="pc" />
            </aop:aspect>
    
        </aop:config>
    </beans>

    demo测试:

    /**
     * Created by YuKai Fan on 2018/9/28.
     */
    @ContextConfiguration("classpath:com/javaweb/springAOP/applicationContext.xml")
    public class Demo {
        @Resource(name="userService")//已经获取到代理对象
        private UserService us;
    
        @Test
        public void fun() {
            us.save();
        }
    
    }

    测试结果:

  • 相关阅读:
    扫描线
    Assign the task HDU
    Can you answer these queries? HDU
    Tunnel Warfare HDU
    Mayor's posters POJ
    not friendly,
    招财铃:即时通信 openfire ,
    不再是可怕的汇编,
    转:宏指令,
    build path,
  • 原文地址:https://www.cnblogs.com/FanJava/p/9714777.html
Copyright © 2011-2022 走看看