Spring4学习回顾之路11-AOP
Srping的核心除了之前讲到的IOC/DI之外,还有一个AOP(Aspect Oriented Programming:面向切面编程):通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。主要的功能: 日志记录,性能统计,安全控制,事务处理,异常处理等等。
在介绍AOP之前,先介绍动态代理;代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
案例:基于接口实现的动态代理:JDK动态代理
首先定义一个接口,用于运算的Operation.java
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public interface Operation { //加法
int add(int i,int j); //减法
int sub(int i,int j); }
再来定义实现类OperationImpl.java
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class OperationImpl implements Operation { @Override
public int add(int i, int j) {
return 0;
} @Override
public int sub(int i, int j) {
return 0;
}
}
现在的需求就是用硬编码的形式添加日志,传统的做法是:
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class OperationImpl implements Operation { @Override
public int add(int i, int j) {
System.out.println("两数相加的参数为:" + i + "+" + j);
int result = i+j;
System.out.println("两数相加的结果为:" + result);
return result;
} @Override
public int sub(int i, int j) {
System.out.println("两数相减的参数为:" + i + "-" + j);
int result = i-j;
System.out.println("两数相减的结果为:" + result);
return result;
}
}
测试代码和结果为:
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class Test { public static void main(String[] args) { Operation op = new OperationImpl();
System.out.println(op.add(1, 2));
System.out.println(op.sub(4,2));
}
} 两数相加的参数为:1+2
两数相加的结果为:3
3
两数相减的参数为:4-2
两数相减的结果为:2
2
显然这么操作是不合理,但是JDK的动态代理就能很好的解决这个问题;如下所示:
还原最初版本的OperationImplProxy.java
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class OperationImplProxy implements Operation { @Override
public int add(int i, int j) {
int result = i+j;
return result;
} @Override
public int sub(int i, int j) {
int result = i-j;
return result;
}
}
编写代理类OperationProxy.java:
package com.lql.proxy; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays; /**
* @author: lql
* @date: 2019.10.29
* Description:
* Created with IntelliJ IDEA
*/
public class OperationProxy { //代理对象
private Operation target; public OperationProxy(Operation target) {
this.target = target;
} //获取代理对象
public Operation getInstance() { Operation proxy = null; ClassLoader loader = target.getClass().getClassLoader(); Class[] clazz= target.getClass().getInterfaces(); InvocationHandler in = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName();
System.out.println("方法名:" + name + ",参数:" + Arrays.asList(args));
Object result = method.invoke(target, args);
System.out.println("结果:" + result);
return result;
}
};
proxy = (Operation) Proxy.newProxyInstance(loader, clazz, in);
return proxy;
}
}
测试类和结果:
package com.lql.proxy; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class Test { public static void main(String[] args) {
// Operation op = new OperationImpl();
// System.out.println(op.add(1, 2));
// System.out.println(op.sub(4,2)); Operation target = new OperationImplProxy();
Operation operation = new OperationProxy(target).getInstance(); System.out.println(operation.add(1, 2));
System.out.println(operation.sub(4, 2));
}
} 方法名:add,参数:[1, 2]
结果:3
3
方法名:sub,参数:[4, 2]
结果:2
2
至此JDK的动态代理demo完成了,这里就不详细介绍每一步了。回归正题:AOP:面向切面编程,相较于传统的OOP(面向对象编程)来说是个补充;说到AOP那就不得不提及它的相关术语了:
-切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
-通知(advice): 切面必须要完成的功能
-目标(target) : 被通知的对象
-代理(Proxy) :向目标对象应用通知之后创建的对象
-连接点(Joinpoint): 程序执行的某个特定位置
-切点(pointcut):每个类都拥有多个连接点
关于AOP的基础配置先出个总图有个印象:
在Spring2.0以上的版本中,可以使用基于AspectJ注解或者基于XML配置的AOP;其中AspectJ是Java社区里最完整流行的AOP框架。
一:基于注解方式:
①:前置通知:
先导入需要的jar包
建立Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--配置自动扫描的包-->
<context:component-scan base-package="com.lql.spring07"/> <!--使AspectJ注解起作用,自动为匹配的类生成代理对象-->
<aop:aspectj-autoproxy/>
</beans>
建立接口
package com.lql.spring07; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public interface Operation { //加法
int add(int i, int j); //减法
int sub(int i, int j); }
建立接口实现
package com.lql.spring07; import com.lql.proxy.Operation;
import org.springframework.stereotype.Component; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
public class OperationImpl implements Operation { @Override
public int add(int i, int j) {
int result = i+j;
return result;
} @Override
public int sub(int i, int j) {
int result = i-j;
return result;
}
}
测试类和结果
package com.lql.spring07; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
public class Test { public static void main(String[] args) { ApplicationContext app = new ClassPathXmlApplicationContext("aop.xml");
OperationImpl bean = app.getBean("operationImpl", OperationImpl.class);
System.out.println(bean.add(2, 2));
System.out.println(bean.sub(4, 2));
}
} 4
2
这样是没有问题的,现在开始给程序增加日志功能,也就是完成之前动态代理的活;
首先是需要定义切面,并把该切面放入IOC容器中,定义如下:先简单完成,后完善功能,具体语法稍后解释!
package com.lql.spring07; import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect { //声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before() {
System.out.println("方法前被调用");
}
}
再次执行测试类结果:
方法前被调用
4
方法前被调用
2
接下来完善日志信息:
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect {
//声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("方法名称: " + methodName + ",参数为 : " + args);
}
}
结果如下:
方法名称: add,参数为 : [2, 2]
4
方法名称: sub,参数为 : [4, 2]
2
关于@Before(value = "execution(* com.lql.spring07.*.*(..))")后面中的一大串其实就是AspectJ的表达式,具体使用(如果看不懂可以直接写完整的方法):
-里面的组成: execution(修饰符匹配 ? 返回值类型匹配操作类型匹配 ? 名称匹配(参数匹配)抛出异常匹配);
*.*描述的是这个包中所有类中所有的方法,不想全部可以细分
(..)描述的是参数
com.lql.spring07是切入点匹配的包名称,如果多层则可以使用..代替比如:com.lql..service.spring07
第一个*则表示该方法的返回值,任意数据类型,当然前面也能增加访问修饰符public或者private
②:后置通知:
在之前的代码中新增后置通知:
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect { //声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("方法名称: " + methodName + ",参数为 : " + args); } //后置通知:方法执行后执行,无论是否执行完毕
@After("execution(* com.lql.spring07.*.*(..))")
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); System.out.println("方法名称: " + methodName + "执行完毕");
}
}
执行结果为:
方法名称: add,参数为 : [2, 2]
方法名称: add执行完毕
4
方法名称: sub,参数为 : [4, 2]
方法名称: sub执行完毕
2
需要注意的是:在后置通知的时候还不能获取目标方法的处理结果!
③:返回通知:
在原先的LogAspect中继续追加返回通知
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect { //声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("方法名称: " + methodName + ",参数为 : " + args); } //后置通知:方法执行后执行,无论是否执行完毕
@After("execution(* com.lql.spring07.*.*(..))")
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); System.out.println("方法名称: " + methodName + "执行完毕");
} /**
* 返回通知:在方法正常结束时执行的代码
* @param joinPoint 连接点,可以获取详细信息
* @param result 接收到的返回值
*/
@AfterReturning(value = "execution(* com.lql.spring07.*.*(..))",returning = "result")
public void afterReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()计算结果为:" + result);
} }
执行结果:
方法名称: add,参数为 : [2, 2]
方法名称: add执行完毕
add()计算结果为:4
4
方法名称: sub,参数为 : [4, 2]
方法名称: sub执行完毕
sub()计算结果为:2
2
④:异常通知:
先修改OperationImpl的sub(),手动抛出一个异常
@Override
public int sub(int i, int j) {
int result = i-j;
throw new RuntimeException();
}
修改LogAspect:增加异常通知
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect { //声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("方法名称: " + methodName + ",参数为 : " + args); } //后置通知:方法执行后执行,无论是否执行完毕
@After("execution(* com.lql.spring07.*.*(..))")
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); System.out.println("方法名称: " + methodName + "执行完毕");
} /**
* 返回通知:在方法正常结束时执行的代码
* @param joinPoint 连接点,可以获取详细信息
* @param result 接收到的返回值
*/
@AfterReturning(value = "execution(* com.lql.spring07.*.*(..))",returning = "result")
public void afterReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()计算结果为:" + result);
} /**
* 异常通知:在目标方法出现异常时会执行,可以访问到异常对象,也能指定具体的异常后执行代码
* @param joinPoint 连接点,可以获取详细信息
* @param e 捕获异常信息
*/
@AfterThrowing(value = "execution(* com.lql.spring07.*.*(..))",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()异常为 :" +e);
} }
执行结果为:
方法名称: add,参数为 : [2, 2]
方法名称: add执行完毕
add()计算结果为:4
4
方法名称: sub,参数为 : [4, 2]
方法名称: sub执行完毕
sub()异常为 :java.lang.RuntimeException
Exception in thread "main" java.lang.RuntimeException...
⑤:环绕通知:
环绕通知几乎能干上述四种通知,比较全能,就相当于动态代理的全过程,但是不一定是最好的。为了观赏性,取消上述定义的手工抛异常,修改LogAspect.java,取消之前定义好的所有通知,只留下环绕通知:
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Component
@Aspect
public class LogAspect {
/**
* 环绕通知:需要携带ProceedingJoinPoint类型的参数,环绕通知类似于动态代理的全过程
*
* @param join 可以决定是否执行目标方法
* 环绕通知必须由返回值,返回值即为目标方法的返回值
*/
@Around("execution(* com.lql.spring07.*.*(..))")
public Object around(ProceedingJoinPoint join) { Object result = null;
String methodName = join.getSignature().getName();
try {
//前置通知
System.out.println(methodName + "()方法,参数为:" + Arrays.asList(join.getArgs()));
//执行目标方法
result = join.proceed();
//返回通知
System.out.println(methodName + "()方法返回值为:" + result);
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常通知
System.out.println(methodName + "()方法出现的异常为:" + throwable);
}
//后置通知
System.out.println(methodName + "()方法执行完毕");
return result;
} }
执行结果:
add()方法,参数为:[2, 2]
add()方法返回值为:4
add()方法执行完毕
4
sub()方法,参数为:[4, 2]
sub()方法返回值为:2
sub()方法执行完毕
2
如果有异常,那么结果为:
至此,五大通知就告一段落了,我们也可以给切面定义优先级,如果有多个切面,可以在每个切面上使用@Order来标明优先级,比如这样
@Order后面的数值越小,优先级就越高!
所以上述完整的切面代码如下所示:
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Order(2)
@Component
@Aspect
public class LogAspect {
//声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "execution(* com.lql.spring07.*.*(..))")
public void before(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("方法名称: " + methodName + ",参数为 : " + args); } //后置通知:方法执行后执行,无论是否执行完毕
@After("execution(* com.lql.spring07.*.*(..))")
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); System.out.println("方法名称: " + methodName + "执行完毕");
} /**
* 返回通知:在方法正常结束时执行的代码
* @param joinPoint 连接点,可以获取详细信息
* @param result 接收到的返回值
*/
@AfterReturning(value = "execution(* com.lql.spring07.*.*(..))",returning = "result")
public void afterReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()计算结果为:" + result);
} /**
* 异常通知:在目标方法出现异常时会执行,可以访问到异常对象,也能指定具体的异常后执行代码
* @param joinPoint 连接点,可以获取详细信息
* @param e 捕获异常信息
*/
@AfterThrowing(value = "execution(* com.lql.spring07.*.*(..))",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()异常为 :" +e);
} /**
* 环绕通知:需要携带ProceedingJoinPoint类型的参数,环绕通知类似于动态代理的全过程
*
* @param join 可以决定是否执行目标方法
* 环绕通知必须由返回值,返回值即为目标方法的返回值
*/
@Around("execution(* com.lql.spring07.*.*(..))")
public Object around(ProceedingJoinPoint join) { Object result = null;
String methodName = join.getSignature().getName();
try {
//前置通知
System.out.println(methodName + "()方法,参数为:" + Arrays.asList(join.getArgs()));
//执行目标方法
result = join.proceed();
//返回通知
System.out.println(methodName + "()方法返回值为:" + result);
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常通知
System.out.println(methodName + "()方法出现的异常为:" + throwable);
}
//后置通知
System.out.println(methodName + "()方法执行完毕");
return result;
} }
不难看出上面用粗体标识的代码太过重复,以后维护也不容易,所以有个重用切点的概念!使用@PointCut来声明切入点表达式,后面的其他通知使用到的时候直接引用方法名即可,定义如下:
@Pointcut(value ="execution(* com.lql.spring07.*.*(..))")
public void ref(){}
更换后完整的代码:
package com.lql.spring07; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import java.util.Arrays;
import java.util.List; /**
* @author: lql
* @date: 2019.10.29
* Description:
*/
@Order(2)
@Component
@Aspect
public class LogAspect { @Pointcut(value ="execution(* com.lql.spring07.*.*(..))")
public void ref(){} //声明该方法是个前置通知,在目标方法执行前执行
@Before(value = "ref()")
public void before(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName();
List<Object> args = Arrays.asList(joinPoint.getArgs());
System.out.println("方法名称: " + methodName + ",参数为 : " + args); } //后置通知:方法执行后执行,无论是否执行完毕
@After("ref()")
public void after(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); System.out.println("方法名称: " + methodName + "执行完毕");
} /**
* 返回通知:在方法正常结束时执行的代码
* @param joinPoint 连接点,可以获取详细信息
* @param result 接收到的返回值
*/
@AfterReturning(value = "ref()",returning = "result")
public void afterReturn(JoinPoint joinPoint,Object result) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()计算结果为:" + result);
} /**
* 异常通知:在目标方法出现异常时会执行,可以访问到异常对象,也能指定具体的异常后执行代码
* @param joinPoint 连接点,可以获取详细信息
* @param e 捕获异常信息
*/
@AfterThrowing(value = "ref()",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println(name +"()异常为 :" +e);
} /**
* 环绕通知:需要携带ProceedingJoinPoint类型的参数,环绕通知类似于动态代理的全过程
*
* @param join 可以决定是否执行目标方法
* 环绕通知必须由返回值,返回值即为目标方法的返回值
*/
@Around("ref()")
public Object around(ProceedingJoinPoint join) { Object result = null;
String methodName = join.getSignature().getName();
try {
//前置通知
System.out.println(methodName + "()方法,参数为:" + Arrays.asList(join.getArgs()));
//执行目标方法
result = join.proceed();
//返回通知
System.out.println(methodName + "()方法返回值为:" + result);
} catch (Throwable throwable) {
throwable.printStackTrace();
//异常通知
System.out.println(methodName + "()方法出现的异常为:" + throwable);
}
//后置通知
System.out.println(methodName + "()方法执行完毕");
return result;
} }
二:基于配置文件的方式配置AOP
首先统统还原为最原始的样子,去掉所有的注解!xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--配置bean-->
<bean id="operationImpl" class="com.lql.spring08.OperationImpl"></bean> <!--配置切面的Bean-->
<bean id="logAspect" class="com.lql.spring08.LogAspect"></bean> <!--配置AOP-->
<aop:config>
<!--配置切点表达式-->
<aop:pointcut id="pointcut" expression="execution(* com.lql.spring08.*.*(..))"/> <!--配置切面及通知-->
<aop:aspect ref="logAspect" order="2">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
<aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="result"></aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
</aop:aspect>
<!--可以定义多个切面,如这样-->
<aop:aspect ref="logAspect2" order="1">
<!--省略。。。-->
</aop:aspect>
</aop:config>
</beans>
运行结果如:
方法名称: add,参数为 : [2, 2]
方法名称: add执行完毕
add()计算结果为:4
4
方法名称: sub,参数为 : [4, 2]
方法名称: sub执行完毕
sub()计算结果为:2
2
Spring4学习回顾之路11-AOP的更多相关文章
- Spring4学习回顾之路01—HelloWorld
以前公司一直使用的是spring3.0,最近一段时间开始用了4.0,官网上都已经有了5.0,但是很多知识点已经忘了差不多了,趁现在项目不忙写写随笔,一来回顾自己的知识点,二来如果能帮助比我还小白的小白 ...
- Spring4学习回顾之路12-事务
事务:事务就是一系列的动作,它们被当做一个单独的工作单元,这些动作要么全部完成,要么全部不起作用:事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性.事务的四个关键属性(ACI ...
- Spring4学习回顾之路04—引用其他Bean,集合数据注入,内部Bean
引用其他Bean 组件应用程序的Bean经常需要相互协作以完成应用程序的功能,所以要求Bean能够相互访问,就必须在Bean配置文件中指定Bean的引用.在Bean的配置文件中可以用过<ref& ...
- Spring4学习回顾之路10-Spring4.x新特性:泛型依赖注入
泛型依赖注入:Spring 4.x中可以为子类注入子类对应的泛型类型的成员变量的引用. 话语太过抽象,直接看代码案例,依次建立如下代码: User.java package com.lql.sprin ...
- Spring4学习回顾之路09-基于注解的方式配置bean
一:基于注解配置Bean 首先介绍下组件扫描(component scanning): Spring能够从classpath下自动扫描,侦测和实例化具有特定注解的组件. 包括: -@Component ...
- Spring4学习回顾之路08- FactoryBean配置Bean
建立Student.java package com.lql.srping04; /** * @author: lql * @date: 2019.10.28 * Description: */ pu ...
- Spring4学习回顾之路07- 通过工厂方法配置Bean
一:通过静态工厂配置Bean 建立Student.java package com.lql.srping04; /** * @author: lql * @date: 2019.10.28 * Des ...
- Spring4学习回顾之路06- IOC容器中Bean的生命周期方法
SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行特定的任务! Spring IOC容器对Bean的生命周期进行管理的过程: -通过构造器或者工厂方法创建 ...
- Spring4学习回顾之路05—自动装配,Bean的继承,依赖和作用域
自动装配 xml配置里的Bean的自动装配,Spring IOC容器可以自动装配Bean,仅仅需要做的是在<bean>标签里的autowire属性里指定自动装配的模式. ①byType(根 ...
随机推荐
- 微信小程序开发-踩坑
异步请求处理 详情描述: 微信小程序的wx.request({})请求时异步处理,以下代码 wx.reuest({ url:"https://XXXA", method:" ...
- Linux操作系统load average过高,kworker占用较多cpu
Linux操作系统load average过高,kworker占用较多cpu 今天巡检发现,mc1的K8S服务器集群有些异常,负载不太均衡.其中10.2.75.32-34,49的load averag ...
- js闭包小实验
js闭包小实验 一.总结 一句话总结: 闭包中引用闭包外的变量会使他们常驻内存 function foo() { var i=0; return function () { console.log(i ...
- Vue插件编写、用法详解(附demo)
Vue插件编写.用法详解(附demo) 1.概述 简单来说,插件就是指对Vue的功能的增强或补充. 比如说,让你在每个单页面的组件里,都可以调用某个方法,或者共享使用某个变量,或者在某个方法之前执行一 ...
- Swift 数据类型
Swift 提供了非常丰富的数据类型,以下列出了常用了几种数据类型: Int 一般来说,你不需要专门指定整数的长度.Swift 提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同: 在32位 ...
- 常见的RSA套路脚本
工具 rsatool https://github.com/ius/rsatool factordb(分解大素数) http://www.factordb.com python-PyCrypto库 O ...
- 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_01-项目概述-功能构架-项目背景
这个就是博学谷下的 在线教育平台
- 加载selenium库
一.maven的下载.解压以及环境变量配置 1.下载maven: 官网下载地址:http://maven.apache.org/download.cgi 在Files下面下载对应的maven版本(官网 ...
- 解析Python编程中的包结构
解析Python编程中的包结构 假设你想设计一个模块集(也就是一个"包")来统一处理声音文件和声音数据.通常由它们的扩展有不同的声音格式,例如:WAV,AIFF,AU),所以你可能 ...
- JAVA 基础编程练习题24 【程序 24 根据输入求输出】
24 [程序 24 根据输入求输出] 题目:给一个不多于 5 位的正整数,要求:一.求它是几位数,二.逆序打印出各位数字. package cskaoyan; public class cskaoya ...