一,JDK动态代理
适用范围:只能对实现了接口的类进行动态代理
1,1:新建一个接口UserDao,新建实现类UserDaoImpl
import com.imooc.dao.UserDao;
/**
* @author :PanXiuWen
* @date :Created in 2019/12/16 10:36
* @desc:UserDao的实现类UserDaoImpl
*/
public class UserDaoImpl implements UserDao {
public void save(String username,String password)
{
System.out.println("save success + "+username);
}
public void delete() {
System.out.println("delete success");
}
public void update() {
System.out.println("update success");
}
public void find() {
System.out.println("find success");
}
}
1.2 JDK动态代理代码
import com.imooc.dao.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author :PanXiuWen
* @date :Created in 2019/12/16 10:45
*/
public class MyJdkProxy {
/**
*
* @param userDao 待增强的类(UserDao接口的实现类,UserDaoImpl)。这里为啥不直接传UserDaoImpl,而是传它的父类UserDao-->多态。
* 传UserDao时,可以确保传递它的所有子类也都可以。
* 如果传UserDaoImpl,那就只能增强UserDaoImpl里面的方法。
* @return 增强后,产生的代理对象proxy
* @desc: 动态代理,是对接口UserDao的具体实现类,他们中的某些方法进行增强。然后产生一个新的对象,即代理对象proxy
*/
public Object createProxy(final UserDao userDao){
/**
* userDao.getClass().getClassLoader():类加载器
* userDao.getClass().getInterfaces():待增强的类(UserDao接口的实现类,UserDaoImpl),它的实现类
* new InvocationHandler(){..invoke..}:对待增强的类(UserDao接口的实现类,UserDaoImpl)中的某些方法,具体要做什么样子的增强。
*/
Object proxy = Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("权限校验。。。");
if (args[0]=="root"&&args[1]=="password"){
System.out.println("权限校验通过");
}else{
System.out.println("权限校验不通过");
}
//执行父类userDao的方法
return method.invoke(userDao,args);
}
//如果不是save方法,就原封不动的去执行,不需要增强产生proxy对象
return method.invoke(userDao,args);
}
});
return proxy;
}
}
1.3 使用JDK动态代理
/**
* JDK动态代理
*/
@Test
public void test1(){
UserDao userDao = new UserDaoImpl();
MyJdkProxy2 proxy = new MyJdkProxy2();
UserDao proxyUserDao = (UserDao) proxy.createProxy(userDao);
// userDao.save();
// userDao.delete();
// userDao.update();
// userDao.find();
proxyUserDao.save("root","password");
proxyUserDao.delete();
proxyUserDao.update();
proxyUserDao.find();
}
二,CGLIB生成代理
可以去生成一个类,去继承目标类(即针对目标类生成一个它的子类)。因此这种方式可以不用目标类实现了接口。
引入spring的核心jar包,之后就可以使用CGLIB了。
spring-beans spring-core spring-expression spring-context
2.1 创建一个类ProductUser
/**
* @author :PanXiuWen
* @date :Created in 2019/12/16 11:27
*/
public class ProductUser {
public void save(){
System.out.println("save....");
}
public void delete(){
System.out.println("delete....");
}
public void update(){
System.out.println("update....");
}
public void find(){
System.out.println("find....");
}
}
2.2 CGLIB生成代理
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author :PanXiuWen
* @date :Created in 2019/12/16 11:31
*/
public class MyCglibProxy {
public Object createProxy(final ProductUser productUser){
//1,创建核心类
Enhancer enhancer = new Enhancer();
//2,设置父类(为ProductUser生成一个子类)
enhancer.setSuperclass(productUser.getClass());
//3,设置回调
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param proxy
* @param method 子类的方法 对象 [CGLIB生成的类]
* @param args 方法参数
* @param methodProxy 父类的方法 对象 []
* @return
* @throws Throwable
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("权限校验...");
return methodProxy.invokeSuper(proxy,args);
}
//执行父类的方法
return methodProxy.invokeSuper(proxy,args);
}
});
//4,生成代理
Object proxy = enhancer.create();
return proxy;
}
}
2.3 使用 CGLIB代理
/**
* CGLIB生成代理
*/
@Test
public void test2(){
ProductUser user = new ProductUser();
MyCglibProxy myCglibProxy = new MyCglibProxy();
ProductUser cglibProxyUser = (ProductUser)myCglibProxy.createProxy(user);
// user.save();
// user.delete();
// user.update();
// user.find();
cglibProxyUser.save();
}
代理知识总结:
1、Spring在运行期,生成动态代理对象,不需要特殊的编译器
2、Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术为目标Bean执行横向织入
1.若目标对象实现了若干接口,Spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类
3、程序中应优先对接口创建代理,便于程序解耦维护
4、标记为final的方法,不能被代理,因为无法进行覆盖
-JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
-CGLib是针对目标类生成子类,因此类或方法不能使用final的
5、Spring只支持方法连接点,不支持属性连接点