在谈AOP之前,需要了解什么是代理模式。
代理模式分为静态代理和动态代理。
代理模式类似现实世界中的中介,比如租房,中介会为双方提供公共服务,如出租房屋、看房、签合同、收取费用等;通过中介(代理),业务双方就不用操心这些繁琐的事务,房东只要将钥匙交给中介,从中介那里收租金;房客只要从中介那里拿房子,交钱给中介即可。其他事情有中介办妥。
静态代理中的角色:
1.抽象角色:业务的抽象,如租房这一业务,通常由接口或抽象类来实现
2.真实角色:被代理的角色,如房东
3.代理角色:代理真实角色,为业务双方提供服务,执行操作
4.客户:访问代理对象
缺点:一个真实角色就会产生一个代理角色,即将原功能封装到代理类中,代码量翻倍,效率低
下面实现简单的静态代理,这个代理类代理了UserServiceImpl类,并增加了日志功能。
UserService接口:
public interface UserService {
public void add();
public void delete();
public void update();
}
UserServiceImpl类:
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("插入");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("更新");
}
}
代理类:
public class UserServiceProxy implements UserService{
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
public void log(String msg){
System.out.println("执行了:" + msg);
}
}
使用静态代理:
public class Client {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}
可以看到,虽然静态代理可以在不修改源码的情况下增加功能,但每增加一个实现类,就要为其增加一个代理类,这样不便使用,效率低下。
那么如何解决静态代理中的问题呢?答案是使用动态代理。
动态代理使用反射机制,不需要手动编写代理类,而是使用代理调用处理器来生成代理类(动态生成)。
动态代理通常分为基于接口和基于类的动态代理:jdk动态代理(基于接口),cglib(基于类)。
对于之前的UserService接口,我们使用jdk动态代理来实现,它代理的是一个接口,而不是一个类。这里涉及使用反射中的InvocationHandler接口和Proxy类。
编写代理调用处理器:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口,这里用Object代替(实际上应使用UserServiceImpl接口)
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类的实例
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//通过代理类执行方法,返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
}
使用代理:
public class Client {
public static void main(String[] args){
//创建真实角色
UserServiceImpl userService = new UserServiceImpl();
//创建代理生成器
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//设置被代理的接口
handler.setTarget(userService);
//获取代理类实例
UserService proxy = (UserService) handler.getProxy();
//代理对象执行方法
proxy.update();
}
}
接下来我们想增加日志输出功能,在执行方法的时候打印日志,使用动态代理,只要在代理调用处理器中增加log方法即可。
在ProxyInvocationHandler中增加日志输出功能:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
//增加日志功能
private void log(String msg){
System.out.println("执行了:" + msg);
}
cglib是基于类的动态代理,当没有接口时,可以使用cglib来实现动态代理。接下来使用cglib实现对之前的UserServiceImpl类的代理并添加日志功能。
编写代理生成器:
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class ProxyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
log(method.getName());
return methodProxy.invokeSuper(obj, args);
}
private void log(String msg){
System.out.println("执行了:" + msg);
}
}
使用代理:
public class Client {
public static void main(String[] args) {
UserServiceImpl service = new UserServiceImpl();
ProxyMethodInterceptor interceptor = new ProxyMethodInterceptor();
UserServiceImpl proxy = (UserServiceImpl) Enhancer.create(service.getClass(), interceptor);
proxy.delete();
}
}
理解了什么是代理,接下来看什么是AOP。一句话概括,AOP就是在不改动源代码的前提下动态的增强。可以增加具体业务之外的功能。
AOP相关概念:
接下来使用SpringBoot注解实现AOP添加日志功能。
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
UserServiceImpl类:
@Service
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("插入");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("更新");
}
}
Log类:
使用@Aspect注解表示一个切面。Spring AOP提供了@Before,@After,@AfterReturning,@Around等一系列注解来表示切面中执行方法的时刻。并且可以使用JoinPoint来获取连接点即被代理者所执行方法的信息。
@Aspect
@Component
public class Log {
@Before("execution(* com.example.demo.demo04_springaop.UserServiceImpl.*(..))")
public void logBefore(JoinPoint jp){
System.out.println("执行前方法之前:" + jp.getSignature());
}
@After("execution(* com.example.demo.demo04_springaop.UserServiceImpl.*(..))")
public void logAfter(JoinPoint jp){
System.out.println("执行方法之后");
}
}
测试:
为了测试,需要获取容器中托管的javabean。
获取Bean工具类:
public class BeanUtil {
public static ConfigurableApplicationContext applicationContext;
public static <T> T getBean(Class<T> c){
return applicationContext.getBean(c);
}
}
测试类:
public class Client {
public void test(){
//获取已经实例化的接口bean
UserService service = BeanUtil.getBean(UserService.class);
//执行bean中方法
service.add();
}
}
启动类:
@SpringBootApplication
public class SpringdemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringdemoApplication.class, args);
BeanUtil.applicationContext = applicationContext;
new Client().test();
}
}