Spring core模块 :Spring的核心功能,主要是IOC容器,解决对象的创建和处理对象的依赖关系。
Spring web模块:spring对web框架的整合的支持。
Spring Aop模块:面向切面编程。
什么是AOP
AOP(aspect object programming)面向切面编程。
功能:让关注点代码也业务代码分离。
什么是关注点
关注点就是很多重复的代码形成的方法。
什么是切面
关注点组成的类,就是切面类。
面向切面的变成,就是指对很多功能重复的代码的抽象。然后在运行的时候再向业务方法中进行动态的植入。
什么是切入点
通过切入点表达式,指定拦截那些类的那些方法,给指定的类在运行时植入切面类代码。
什么是代理模式
是一种设计模式,提供了对目标对象的另外的访问方式。也就是通过代理来访问目标对象。
如下图,用户对象请求通过代理对象为中间代理人找到目标对象,目标完成任务后,最后还是有代理人返回请求结果。也就是说这里:
目标对象是正真干活的人,
代理对象是针对于这个目标对象做的一些额外的扩展功能的工作。
举例说:华为手机要找胡歌代言新手机,肯定不是直接找胡歌的,当然肯定是找不到的,要是胡歌每天亲自处理这些事,那还有什么时间演电视剧早都嗝屁了,厂商肯定是找到经纪人,由经纪人跟胡歌商量沟通是否代言,如果可以的话再由经纪人出面告知厂商沟通的结果。
代理模式的好处:
可以在目标功能实现的基础上,增加额外的功能,从而扩展目标功能的功能点,但是不影响目标原有功能的实现。
静态代理
现在有这样一个需求,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中它是自动识别判断的。