之前书中的例子代码不全,这里我们通过几个例子来演示说明spring aop
的功能。
一、基本的 AOP 使用
工程结构
首先定义切面 SecurityHandler.java
package win.iot4yj.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
*
* @author yj
* 这个类就是一个切面
*/
@Aspect
@Component
public class SecurityHandler {
//定义一个切点,只是相当于一个标识。即匹配所有add开头的方法,参数和返回值任意。而allAddMethod方法不能有返回值
@Pointcut("execution(* add*(..))")
private void allAddMethod(){}
//这个方法就相当于一个通知,标识在哪个切入点织入
@Before("allAddMethod()")
private void checkSevurity(){
System.out.println("-------安全性检查-------");
}
}
说明:切点的标识方法可以有参数,但不能有返回值。
业务类 UserManager.java UserManagerImpl.java
package win.iot4yj.spring.manager;
public interface UserManager {
public void addUser(String username, String password);
}
package win.iot4yj.spring.manager;
import org.springframework.stereotype.Component;
@Component("userManager")
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
System.out.println("-------添加用户-------");
}
}
配置类 Config.java
package win.iot4yj.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import win.iot4yj.spring.aspect.SecurityHandler;
import win.iot4yj.spring.manager.UserManager;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackageClasses={UserManager.class, SecurityHandler.class})
public class Config {
}
测试 IoCTest.java
package win.iot4yj.spring.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import win.iot4yj.spring.manager.UserManager;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=win.iot4yj.spring.config.Config.class)
public class IoCTest {
@Autowired
private UserManager userManager;
@Test
public void test01() {
userManager.addUser("张三", "123");
}
}
说明:这里我们使用的是前置通知。会在添加用户之前进行安全性检查。
二、环绕通知
下面看环绕通知,没有给出的代码和之前一样。
SecurityHandler.java
package win.iot4yj.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
*
* @author yj
* 这个类就是一个切面
*/
@Aspect
@Component
public class SecurityHandler {
//定义一个切点,只是相当于一个标识。即匹配所有add开头的方法,参数和返回值任意。而allAddMethod方法不能有返回值
@Pointcut("execution(* add*(..))")
private void allAddMethod(){}
//这个方法就相当于一个通知,标识在哪个切入点织入
@Around("allAddMethod()")
private void checkSevurity(ProceedingJoinPoint jp){
System.out.println("-------安全性检查之前-------");
try {
jp.proceed();//调用被通知的方法
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("-------安全性检查之后-------");
}
}
三、传递参数
可以从之前的测试代码中看到,添加用户肯定是传递了用户名和密码的,这里我们将其传递到通知中去。
SecurityHandler.java
package win.iot4yj.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
*
* @author yj
* 这个类就是一个切面
*/
@Aspect
@Component
public class SecurityHandler {
//定义一个切点,只是相当于一个标识。即匹配所有add开头的方法,参数和返回值任意。而allAddMethod方法不能有返回值
@Pointcut("execution(* add*(..)) && args(username, password)")
private void allAddMethod(String username, String password){}
//这个方法就相当于一个通知,标识在哪个切入点织入
@Around("allAddMethod(username, password)")
private void checkSevurity(ProceedingJoinPoint jp, String username, String password){
System.out.println("-------安全性检查之前-------");
try {
jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("用户名: " + username + ", 密码: " + password);
System.out.println("-------安全性检查之后-------");
}
}
说明:这里要注意的是一定要使用args(username, password)
进行标识,这标明相关的参数也会传递到通知中去。
四、引入新功能
这里首先定义要引入的新功能的类和接口
package win.iot4yj.spring.manager;
public interface NewFun {
public void newFunction();
}
package win.iot4yj.spring.manager;
import org.springframework.stereotype.Component;
@Component
public class NewFunImpl implements NewFun {
@Override
public void newFunction() {
System.out.println("-----引入的新功能-----");
}
}
切面 SecurityHandler.java
package win.iot4yj.spring.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import win.iot4yj.spring.manager.NewFun;
import win.iot4yj.spring.manager.NewFunImpl;
/**
* @author yj
* 这个类就是一个切面
*/
@Aspect
@Component
public class SecurityHandler {
@DeclareParents(value = "win.iot4yj.spring.manager.UserManager+",
defaultImpl = NewFunImpl.class)
public static NewFun n;
//定义一个切点,只是相当于一个标识。即匹配所有add开头的方法,参数和返回值任意。而allAddMethod方法不能有返回值
@Pointcut("execution(* add*(..)) && args(username, password)")
private void allAddMethod(String username, String password){}
//这个方法就相当于一个通知,标识在哪个切入点织入
@Around("allAddMethod(username, password)")
private void checkSevurity(ProceedingJoinPoint jp, String username, String password){
System.out.println("-------安全性检查之前-------");
try {
jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("用户名: " + username + ", 密码: " + password);
System.out.println("-------安全性检查之后-------");
}
}
说明:相对于之前的代码,这里添加了引入新功能的配置。具体说明参考前一小节文章。
测试 IoCTest.java
package win.iot4yj.spring.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import win.iot4yj.spring.manager.NewFun;
import win.iot4yj.spring.manager.UserManager;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=win.iot4yj.spring.config.Config.class)
public class IoCTest {
@Autowired
private UserManager userManager;
@Autowired
ApplicationContext applicationContext;
@Test
public void test01() {
userManager.addUser("张三", "123");
NewFun newFun = (NewFun) applicationContext.getBean("userManager");
newFun.newFunction();
}
}
说明:这里首先自动注入了应用上下文类ApplicationContext
,spring
中很多类,如上下文类、环境类Environment
类都可以这样获得。而其实通过ApplicationContext
就可以获得应用中已存在的bean
,所以这里的UserManager bean
自动注入可以省略。此时就可以使用新引入的功能了。