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();
        }
    
    }

    测试结果:

  • 相关阅读:
    168. Excel Sheet Column Title
    171. Excel Sheet Column Number
    264. Ugly Number II java solutions
    152. Maximum Product Subarray java solutions
    309. Best Time to Buy and Sell Stock with Cooldown java solutions
    120. Triangle java solutions
    300. Longest Increasing Subsequence java solutions
    63. Unique Paths II java solutions
    221. Maximal Square java solutions
    279. Perfect Squares java solutions
  • 原文地址:https://www.cnblogs.com/FanJava/p/9714777.html
Copyright © 2011-2022 走看看