总结:SpringAOP(一、概念和代理)

Spring core模块 :Spring的核心功能,主要是IOC容器,解决对象的创建和处理对象的依赖关系。
Spring web模块:spring对web框架的整合的支持。
Spring Aop模块:面向切面编程。

什么是AOP

AOP(aspect object programming)面向切面编程。
功能:让关注点代码也业务代码分离。

什么是关注点

关注点就是很多重复的代码形成的方法。

什么是切面

关注点组成的类,就是切面类。
面向切面的变成,就是指对很多功能重复的代码的抽象。然后在运行的时候再向业务方法中进行动态的植入。

什么是切入点

通过切入点表达式,指定拦截那些类的那些方法,给指定的类在运行时植入切面类代码。

什么是代理模式

是一种设计模式,提供了对目标对象的另外的访问方式。也就是通过代理来访问目标对象。

如下图,用户对象请求通过代理对象为中间代理人找到目标对象,目标完成任务后,最后还是有代理人返回请求结果。也就是说这里:
目标对象是正真干活的人,
代理对象是针对于这个目标对象做的一些额外的扩展功能的工作。
举例说:华为手机要找胡歌代言新手机,肯定不是直接找胡歌的,当然肯定是找不到的,要是胡歌每天亲自处理这些事,那还有什么时间演电视剧早都嗝屁了,厂商肯定是找到经纪人,由经纪人跟胡歌商量沟通是否代言,如果可以的话再由经纪人出面告知厂商沟通的结果。
总结:SpringAOP(一、概念和代理)代理模式的好处:
可以在目标功能实现的基础上,增加额外的功能,从而扩展目标功能的功能点,但是不影响目标原有功能的实现。

静态代理
现在有这样一个需求,UserService本身有一个添加用户的方法,现在想在添加用户的这个方法前后分别添加事务。而这个添加用户的方法也是UserService调用UserDao中的数据库执行方法操作。也就是说现在要扩展UserDao中的方法。假设我们dao的方法是如下这样:
DAO接口:
package java;

/**
 * @explain:IuserDao
 * @author:星
 * @date:2018/12/11
 * @create by IntelliJ IDEA
 */
public interface IuserDao {

    public  void save() ;
}

DAO实现:

package java;

/**
 * @explain:userDao
 * @author:jimmy
 * @date:2018/12/11
 * @create by Intellij Idea
 */
public class UserDao implements  IuserDao {

    public void save(){
        System.out.println(">>>>>数据库保存成功<<<<<");
    }

}

如上,现在想在save方法中添加事务的处理。可以写成如下这样:

package java;

/**
 * @explain:userDao
 * @author:jimmy
 * @date:2018/12/11
 * @create by Intellij Idea
 */
public class UserDao implements  IuserDao {

    public void save(){
    	 System.out.println("======开启事务<<<<<");
        System.out.println(">>>>>数据库保存成功<<<<<");
        System.out.println("======关闭事务<<<<<");
    }

}

但是这样写的话,需要修改UserDao本身,现在通过静态代理的方式实现,不修改UserDao本身方法,同时扩展UserDao的功能,实现上述事务的开启和关闭操作。
首先新建一个代理类UserStaticProxy,这里UserDao就相对于目标对象,UserStaticProxy为静态代理对象。

package java.staticProxy;

import java.IuserDao;

/**
 * @explain:静态代理对象
 * 代理对象必须要实现跟目标对象一致的接口
 * @author:jimmy
 * @date:2018/12/11
 * @create by Intellij Idea
 */
public class UserStaticProxy implements  IuserDao{


    private IuserDao target ;
    public UserStaticProxy(IuserDao target ){
        this.target = target ;
    }

    @Override
    public void save() {
        System.out.println("===开始事务");
        target.save();
        System.out.println("===结束事务");
    }
}

很明显这样写,使得目标对象的功能得到扩展,但是同时没有修改目标对象的代码。

静态代理的优点:不修改原有功能代码的基础功能上,对原有功能实现扩展。
静态代理的缺点:
(1)因为代理类必须要实现原有功能对应的接口,所以如果有很多接口的时候,要同时实现很多个接口。
(2)一旦接口中的功能修改了,目标对象的中功能和代理对象中的功能都需要修改,不方便维护。

为了解决上述静态代理的缺点,可以使用动态代理。

动态代理

特点:代理对象不需要实现接口。代理对象的生成,使用到了jdk的api,jdk中已经实现了对应的类。这样就可以动态的在内存中创建代理对象。所以动态代理也叫JDK代理。

同样还是上述的例子,如下:
目标对象的代码这里不写了,动态代理对象的代码如下:

package java.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @explain:动态代理工厂
 * @author:jimmy
 * @date:2018/12/11
 * @create by Intellij Idea
 */
public class UserDynamicProxy {

    //需要有一个目标对象,可以传入各种不同的目标对象。
    private Object target ;

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

    //生成正真的代理对象
    public Object getDynamicProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象的类加载器
                target.getClass().getInterfaces(),  //目标对象实现的所有的接口
                new InvocationHandler() {//事件处理器
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("开始事务======="); //对目标对象进行扩展
                        //调用目标对象target中的方法
                        //   method.invoke(target,args);
                        Object results = method.invoke(target, args);
                        System.out.println("事务提交======");//对目标对象进行扩展
                        //results是调用目标对象执行返回的结果
                        return results;
                    }
                }
        );
    }
}

看一下Proxy中对应方法的源码:

  @CallerSensitive
    public static Object newProxyInstance(
    					    ClassLoader loader,//传入的要进行代理的目标对象的类加载器
                                          Class<?>[] interfaces,//传入的要进行代理的目标对象实现的所有接口
                                          InvocationHandler h)//事件处理,这里调用目标对象的方法,并且进行扩展,并返回调用目标对象执行返回的结果
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

总结:如上,可以看出来,如果是动态代理的方式,如果我们修改目标对象中的执行方法的时候,这个时候代理对象不需要再做修改。
但是有一个局限性,如果目标对象中,没有实现接口,就不能用动态代理的方式
所以就有了cglib代理
第三方cglib代理
概念:子类代理,在内存中构建一个一个子类对象从而实现对目标对象的扩展。
实现的步骤:
引用.jar文件。(引用过spring-core等核心包文件后就不要再引用)

package java.dynamicProxy;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @explain:cglib子类代理工厂(这里的作用是对目标对象userDao在内存中动态创建一个代理)
 * @author:jimmy
 * @date:2018/12/11
 * @create by Intellij Idea
 */
public class ProxyFactory implements MethodInterceptor {

        //引入目标对象
    private Object target ;
        //通过构造获取目标对象
    public ProxyFactory(Object target ){
     this.target=target ;
    }

        //创建一个代理对象
    public Object getProxyInstance(){
        //第一步:用到工具类,用它来设置产生子类的代理
        Enhancer enhancer = new Enhancer() ;
        //第二步:设置父类(要产生哪个类的代理)
        enhancer.setSuperclass(target.getClass());
        //第三步:设置回调方法
        enhancer.setCallback(this);
        //第四步:创建代理对象
        return enhancer.create() ;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //在这里可以加一些对目标对象的扩展方法

        //执行目标对象的方法
        Object resultValue = method.invoke(target, args);

        return resultValue ;
    }
}

总结:
cglib代理是在内存中动态的创建目标对象的子类,所以目标对象不能是final的,因为final修饰是无法创建子类的。
并且如果目标对象中的方法是final修饰的,是无法被拦截的,就无法实现cglib动态代理。

总结:
如果加入容器中的目标对象,
有实现接口的,则用JDK动态代理。
如果目标对象没有实现接口,则推荐用cglib代理。
这个在Spring AOP中它是自动识别判断的。