在实际开发中,我们往往会遇到对已有功能做扩展的情况,我们需要在不对源码进行修改的情况下扩展功能,在没有spring-aop的情况下我们应该这么做:
情景1:我们现在要对AServiceImpl(实现了AService接口)中的login方法进行扩展,这种情形我们可以使用代理模式来做,过程如下:创建一个BServiceImpl类并实现AService接口,并重现doLogin方法。
class BServiceImpl implements AService{
private AService trueObj;
BServiceImpl(AService service){
trueObj = service;
}
@override
public void doLogin(){
sout("扩展前");
trueObj.doLogin();
sout("扩展后");
}
}
而AOP的出现就可以省略BServiceImpl的创建,我们指定一个类的方法为切点,然后我们需要对这个切点进行扩展,扩展又分为扩展前、扩展后、环绕、和异常等部分,我们可以对其进行声明然后指向切点。
id为before的bean是前置通知,在真实方法(doLogin)执行前执行,id为after的bean是后置通知在真实方法执行后执行
代码如下
真实对象:要进行功能扩展的对象,相当于AServicImpl对象
代理对象:完成功能扩展的对象,相当于BServiceImpl对象
在spring-aop中,代理对象是动态创建的
切点:要进行功能扩展的方法,相当于doLogin方法
前置通知方法:在切点执行之前就执行的扩展方法
后置通知方法:在切点执行后才执行的扩展方法
切面:有前置通知+切点+后置通知形成的横向切面
织入:形成切面的过程
AOP:面相切面编程
使用:
方法:
参数:
public class LoginBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("start login....");
}
}
使用:
方法:
参数:
public class LoginAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("Login end");
System.out.println("Login User:"+o);
}
}
使用:
方法:
参数:
public class LoginAround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object[] arguments = methodInvocation.getArguments();
String user = (String) arguments[0];
String pwd = (String) arguments[1];
if(user==null || user == "" || pwd==null || pwd == ""){
System.out.println("username and pwd can't be null");
return null;
}
Object emp = methodInvocation.proceed();
if(emp != null){
System.out.println("login success");
}
return emp;
}
}
使用:
方法:
public class MyThrow implements ThrowsAdvice {
public void afterThrowing(Exception e){
System.out.println("我是异常通知:"+e.getMessage());
}
}
//前置通知需要实现接口MethodBeforeAdvice
public class LoginBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
Logger logger = Logger.getLogger(LoginBefore.class);
logger.debug(objects[0]+"发起登陆");
}
}
//后置通知需要实现接口AfterReturningAdvice
public class LoginAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
if(o!=null){
Logger logger = Logger.getLogger(LoginAfter.class);
logger.debug(objects[0]+"登陆成功");
}
}
}
spring-aop可以通过切点中execution中的路径自动匹配之前的bean,并根据实现的接口动态创建代理对象
@WebServlet("/UserServlet")
public class UserServlet extends BaseServlet{
private LoginService service;
@Override
public void init() throws ServletException {
ApplicationContext ac= WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
service= (LoginService) ac.getBean("lc");//通过获取切点动态获得代理对象
}
public void login(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String uname = req.getParameter("uname");
String pwd = req.getParameter("pwd");
User user = service.doLogin(uname, pwd);
req.getSession().setAttribute("user",user);
resp.sendRedirect(this.getServletContext().getContextPath()+"/main.jsp");
resp.getWriter().print((user!=null) + "");
}
}
public class MyAdvice {
//前置通知方法
@Before("execution(* com.bjsxt.pojo.Student.testStu(..))")
public void before(){
System.out.println("我是前置通知");
}
//后置通知方法
public void after(){
System.out.println("我是后置通知");
}
//环绕通知方法
public Object myRound(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕--前");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕--后");
return proceed;
}
//异常通知方法
public void myThrow(){
System.out.println("我是异常通知");
}
}
public class Student implements StudentInterface{
@Override
public String testStu() {
//int i=5/0;
System.out.println("我是Student "+"的学生方法,我被执行了,,,,:");
return "我是真实对象";
}
}
//传入通知类
-
<!–声明切点–>
<!–声明前置通知方法–>-->
<!–声明后置通知方法–>
<!–声明环绕通知–>
<!–声明异常通知–>
//扫描注解的范围,提高程序效率
//自动创建代理类,默认为jdk方式
@Component
public class Student implements StudentInterface{
//切点的创建不是必要的,但最好还是声明一下
@Pointcut("execution(* com.bjsxt.pojo.Student.testStu(..))")
@Override
public String testStu(String name) {
//int i=5/0;
// System.out.println("我是Student "+"的学生方法,我被执行了,,,,:");
System.out.println("我是Student "+name+"的学生方法,我被执行了,,,,:");
return "我是真实对象";
}
}
@Component //创建它的bean
@Aspect //声明它为通知类
public class MyAdvice {
//前置通知方法
@Before("execution(* com.bjsxt.pojo.Student.testStu(..))")
public void before(){
System.out.println("我是前置通知");
}
//后置通知方法
@After("execution(* com.bjsxt.pojo.Student.testStu(..))")
public void after(){
System.out.println("我是后置通知");
}
//环绕通知方法
@Around("execution(* com.bjsxt.pojo.Student.testStu(..))")
public Object myRound(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕--前");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕--后");
return proceed;
}
//异常通知方法
@AfterThrowing("execution(* com.bjsxt.pojo.Student.testStu())")
public void myThrow(){
System.out.println("我是异常通知");
}
}
无论是哪种方式的aop,都需要创建通知类和真实类的bean,需要一个给定的切点并且通知方法需要被声明是哪种类型的通知方法
对于有参数的切点使用SchemaBase方式
对于无参数的切点或者不需要参数的通知方法使用aspectJ方式