-
什么是代理模式
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
举个例子来说明:假如说我现在想要办一个婚礼,虽然我还要去找酒店,还要去布置现场,还要去找婚车等,但是这确实太浪费时间和精力了。我只是想愉快轻松的结个婚,为什么我还要额外做这么多事呢?于是我就通过婚庆公司来帮我们订酒店、布置现场、,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:
-
静态代理
测试环境搭建:
1.编写一个共同的接口Marry
public interface Marry {
void Marry();
}
2.编写要结婚的新人Couple.java
public class Couple implements Marry{
@Override
public void Marry() {
System.out.println("结婚的人入场");
}
}
3.编写婚庆公司类WeddingServices.java
public class WeddingServices implements Marry {
Couple couple;
public WeddingServices(Couple couple) {
super();
this.couple = couple;
}
@Override
public void Marry() {
System.out.println("婚庆公司:");
System.out.println("找酒店和婚车");
System.out.println("布置现场");
couple.Marry();
System.out.println("收拾现场,婚礼结束");
}
}
4.编写代理测试类testProxy.java
public class testProxy {
public static void main(String[] args) {
WeddingServices weddingServices = new WeddingServices(new Couple());
weddingServices.Marry();
}
}
-
JDK动态代理
什么是JDK动态代理?
使用jdk的反射机制,创建对象的能力, 创建的是代理类的对象。 而不用你创建类文件。不用写java文件。动态:在程序执行时,调用jdk提供的方法才能创建代理类的对象。JDK动态代理,必须有接口,目标类必须实现接口, 没有接口时,需要使用CGLIB动态代理
哈哈,我们废话不多说,我们直接上代码
1.我们还是以结婚为例子来讲解,我们先建一个共同的结婚接口
public interface Marry {
void Marry();
}
2.然后再建一个结婚对象Couple类
public class Couple implements Marry{
@Override
public void Marry() {
System.out.println("结婚的人入场");
}
}
· 3.最后我们建立JDK动态代理类DynamicProxy.java
public class DynamicProxy{
private Object target;
public DynamicProxy(Object target) {
super();
this.target = target;
}
//通过Proxy.newProxyInstance获取代理对象
public Object getProxy() {
//获取类加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//得到目标对象的实现数组
Class[] interfaces = target.getClass().getInterfaces();
//获取InvocationHandler接口
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* proxy代表实例
* method表示目标对象的方法,可以通过getmethod来获取
* args表示参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用目标对象中的方法
method.invoke(target, args);
return null;
}
};
Object proxy = Proxy.newProxyInstance(classLoader,interfaces, invocationHandler);
return proxy;
}
}
4.最后一步我们来写一个测试类testDynaminProxy
public class testDynaminProxy {
public static void main(String[] args) {
DynamicProxy proxy = new DynamicProxy(new Couple());
Marry couple = (Marry) proxy.getProxy();
couple.Marry();
}
}
-
CGLIB动态代理
什么是CGLIB代理?
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
下面我们来写一个CGLIB动态代理代码看看
1.环境准备,准备一些需要的Jar
cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.
cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.
2.目标类Person.java
public class Person {
public void eat() {
System.out.println("干饭了。。。。");
}
}
3.CGLIB代理类CglibProxy.java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor{
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
public Object getProxy() {
//作为代理对象目标类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
//生成类
return enhancer.create();
}
/**
*
* @param o 代理对象
* @param method 代理对象方法
* @param objects 代理对象参数
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("饭前洗手");
Object resulttarget = method.invoke(target, objects);
System.out.println("饭后洗手");
return resulttarget;
}
}
4.最后我们建立一个测试类TestCglibProxy.java
public class TestCglibProxy {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy(new Person());
Person person = (Person) cglibProxy.getProxy();
person.eat();
}
}
-
SpringAOP
1.AOP是什么?
首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。
- 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
- 所谓的周边功能,比如性能统计,日志,事务管理等等
周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面
在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP,底层是JDK代理加CGLIB代理
2. AOP当中的概念:
- 切入点(Pointcut)
在哪些类,哪些方法上切入 - 通知(Advice)
在方法执行的什么实际(when:方法前/方法后/方法前后)做什么(what:增强的功能) - 切面(Aspect)
切面 = 切入点 + 通知,通俗点就是:在什么时机,什么地方,做什么增强! - 织入(Weaving)
把切面加入到对象,并创建出代理对象的过程。(由 Spring 来完成)
一张图来理解AOP的切入思想
最后我们用代码来演示
1.环境准备,在Spring的配置文件中AOP和CONTEXT加入约束
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="top.yunhuisu"/>
<-- 代理开启!-->
<aop:aspectj-autoproxy/>
</beans>
2.建立一个切面类Aspect.java,最后一个注释了的是Around可以代替前面的全部
package top.yunhuisu.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@org.aspectj.lang.annotation.Aspect
/**
* 1、execution(): 表达式主体。
* 2、第一个*号:表示返回类型, *号表示所有的类型。
* 3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,top.yunhuisu.service.*包、子孙包下所有类的方法。
* 4、第二个*号:表示类名,*号表示所有的类。
* 5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
*/
public class Aspect {
//切面点方法
@Pointcut("execution( * top.yunhuisu.service.*.*(..))")
public void Cut() {
}
@Before(value = "Cut()")
public void before() {
System.out.println("前置通知。。。");
}
@AfterReturning(value = "Cut()")
public void afterReturning() {
System.out.println("返回通知。。。");
}
@AfterThrowing(value = "Cut()", throwing = "e")
public void afterThrowing(Exception e) {
System.out.println(e.getMessage());
}
@After(value = "Cut()")
public void after() {
System.out.println("最后通知。。。");
}
/*
@Around(value = "Cut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) {
try {
System.out.println("前置通知。。。");
proceedingJoinPoint.proceed();
System.out.println("返回通知。。。");
}catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知"+throwable.getMessage());
}finally {
System.out.println("最后通知。。。");
}
}
*/
}
3.最后建立一个测试类testSpringaop.java
public class testSpringaop {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("Application.xml");
UserService userService = app.getBean("userService",UserService.class);
userService.Advice();
}
}