Java Spring 基础

IOC(DI)

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。
1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦。

IOC的别名:依赖注入(DI Dependency Injection),通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。类似于电脑主机的USB接口,遵循USB接口标准接入各种外设

IOC实现:反射(Reflection),就是根据给出的类名(字符串方式)来动态地生成对象。我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中或者扫描注解给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件或者注解来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

bean概念:由IOC容器管理的那些组成你应用程序的对象我们就叫它Bean, Bean就是由Spring容器初始化、装配及管理的对象。Bean之间的依赖关系这就需要配置元数据,在Spring中由BeanDefinition代表

实现步骤:

  1. 加载并且保存Spring配置文件路径信息然后保存到configLocation中
  2. 刷新Spring上下文环境
  3. 创建并且载入DefaultListableBeanFactory(即BeanFactory)
  4. 根据DefaultListableBeanFactory创建XMLBeanDefinitionReader,用于后面读取xml配置文件信息
  5. 创建BeanDefinitionDelegate代理类,用于解析xml配置信息
  6. 解析xml中配置的等不同的标签信息,以便于可以使用不同的解析器进行解析
  7. 通过XMLBeanDefinitionReader结合location路径信息读取Resources资源信息
  8. 使用BeanDefinitionDelegate代理类解析BeanDefinition bean元素并且依次进行实例化操作,实例化完毕之后将Bean信息注册(put)到BeanDefinitionMap中以便于可以下次继续使用

各个原理概念简介:

  1. BeanDefinition 对依赖翻转模式中管理对象依赖关系的数据抽象
  • 实现依赖翻转功能的核心数据结构
  • 依赖翻转功能都是围绕对BeanDefinition 处理完成的
  • 有了这些BeanDefinition 基础数据结构,容器才能发挥作用
  1. BeanFactory 定义了IOC 容器的基本功能规范
  • IOC容器最基本形式
  • 遵守的基本契约
  • 最底层最基本编程规范
  • 仅仅是一个接口
    DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等都是附加了某种功能的具体实现
    DefaultListableBeanFactory 包含了ioc 的重要功能(容器系列中的一个基本产品)
  1. FactoryBean和BeanFactory
  • 前者是一个Bean
  • 后者是IOC容器(对象工厂),一个Factory
  1. XmlBeanFactory原理
  • 提供了最基本的ioc 容器的功能
  • 读取XML 形式 BeanDefinition 的ioc 容器
  • XMLDefinitionReader 处理xml 形式的BeanDefinition
  • 信息来源有 Resource类来给出
  • Resource 类是封装io操作的类
  • loadBeanDefinitions 方法:ioc 容器初始化重要方法
  1. BeanFactory 和ApplicationContext
  • BeanFactory 实现是ioc 容器基本形式
  • ApplicationContext 是高级形式(具有增强特性)
  1. IOC 容器初始化
  • BeanDefinition 的Resource定位、载入和注册
  • 这三个过程使用不同模块完成:
    • BeanDefinition 的资源定位由ResourceLoader通过统一形式Resource接口完成
      • BeanDefinition 存在形式:FileSystemResource、ClassPathResource等
    • BeanDefinition 载入:把用户定义好的Bean表示成IOC容器的数据结构,即BeanDefinition
      • POJO对象在IOC 容器内部的抽象
    • BeanDefinition 注册:BeanDefinitionRegistry 接口的实现类来完成的
      • IOC 容器内部是通过一个HashMap 持有这些对象的
    • IOC 容器初始化和上下文初始化一般不包括依赖注入
      • lazyInit属性,会在第一次getBean 时注入

beanFactory和ApplicationContext的区别:

  1. 通过ApplicationContext上下文容器:BeanFactory需要手动注册,而ApplicationContext则是自动注册,当在加载xml配置文件时,配置文件中的配置的bean已经被实例化
  2. BeanFactory:在加载配置文件时,配置文件中的bean不被实例化,只有当通过getBean(),获取bean实例的时候才被创建。BeanFactory提供了IOC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。

循环依赖处理

1
2
3
4
5
6
7
8
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
...
}

类图

BeanFactory
DefaultListableBeanFactory
ApplicationContext

BeanDefinition
RootBeanDefinition
XMLBeanDefinitionReader

容器初始化
XMLWebApplicationContext

AOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

单一职责原则

使用场景

  • 事务,通过抛出异常回滚
  • 日志
  • 权限
  • 错误处理
  • 处理缓存

相关概念

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强,通知):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是环绕执行的代码。
  • Target(目标对象):织入 Advice 的目标对象。
  • AOP代理(AOP Proxy): AOP框架创建的对象,将通知应Advice用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象为目标对象的业务逻辑功能加上被切入的切面所形成的对象。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  • Weaving(织入):将 Aspect切面应用到Target对象, 并创建 Adviced object 的过程

在 Spring AOP 中 Joint point 指代的是所有方法的执行点, 而 point cut 是一个描述信息, 它修饰的是 Joint point, 通过 point cut, 我们就可以确定哪些 Joint point 可以被织入 Advice.

AOP中的Joinpoint可以有多种类型:构造方法调用,字段的设置和获取,方法的调用,方法的执行,异常的处理执行,类的初始化。也就是说在AOP的概念中我们可以在上面的这些Joinpoint上织入我们自定义的Advice,但是在Spring中却没有实现上面所有的joinpoint,确切的说,Spring只支持方法执行类型的Joinpoint。
具体说就是需要做某些处理(如打印日志、处理缓存等等)的连接点,AspectJ中的注解:

  • @annotation 限定有指定注解的连接点
  • execution() 用于匹配是连接点的执行方法
  • target 限定连接点匹配目标对象为指定类型

切面编程:
AOP切面概念
各个概念汇总:
AOP概念汇总
类图:
AOP类图
Advice 的类型

  • @Before ,before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
  • @AfterReturning, after return advice, 在一个 join point 正常返回后执行的 advice
  • @AfterThrowing, after throwing advice, 当一个 join point 抛出异常后执行的 advice
  • @After, after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
  • @Around, around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
  • introduction

代理对象

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。

CGLIB和Java动态代理的区别
Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

CGLIB:
cglib概念

ASM框架

ASM框架是一个致力于字节码操作和分析的框架,它可以用来修改一个已存在的类或者动态产生一个新的类。ASM提供了一些通用的字节码转换和分析算法,通过这些算法可以定制更复杂的工具。ASM提供了其它字节码工具相同的功能,但是它更关注执行效率,它被设计的更小更快,它被用于以下项目:

  • openjdk,实现lambda表达式调用, Nashorn编译器
  • Groovy和Kotlin编译器
  • Cobertura 和Jacoco,测量代码范围
  • CGLIB动态代理类

生成代理对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...
/**
*
* 获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised
* 检查上面得到的接口中有没有定义 equals或者hashcode的接口
* 调用Proxy.newProxyInstance创建代理对象
*
*/
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
...
}

代理对象织入
我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
...
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;

TargetSource targetSource = this.advised.targetSource;
Object target = null;

try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
// eqauls()方法,具目标对象未实现此方法
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
// hashCode()方法,具目标对象未实现此方法
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {// Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}

Object retVal;

if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}

// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();// 获得目标对象的类
Class<?> targetClass = (target != null ? target.getClass() : null);

// Get the interception chain for this method.// 获取可以应用到此方法上的Interceptor列表
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {// 如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...// 创建MethodInvocation
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}

// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
}

获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。

从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
public class AdvisedSupport extends ProxyConfig implements Advised {
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
}

实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {
/**
* 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,
* 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断
* 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.
*/
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {

// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
// 查看是否包含IntroductionAdvisor
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
// 这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 将Advisor转化成Interceptor
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
// 检查当前advisor的pointcut是否可以匹配当前方法
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}

return interceptorList;
}

/**
* Determine whether the Advisors contain matching introductions.
*/
private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
for (int i = 0; i < config.getAdvisors().length; i++) {
Advisor advisor = config.getAdvisors()[i];
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(actualClass)) {
return true;
}
}
}
return false;
}

}

这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor

接下来我们再看下得到的拦截器链是怎么起作用的JdkDynamicAopProxy.java:

1
2
3
4
5
6
7
8
if (chain.isEmpty()) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
} else {
// 创建MethodInvocation
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}

从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
...
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 如果Interceptor执行完了,则执行joinPoint
return invokeJoinpoint();
}

Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 如果要动态匹配joinPoint
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;

// 动态匹配:运行时参数是否满足匹配条件
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {

// 执行当前Intercetpor
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.

// 动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 执行当前Intercetpor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
...
}

应用实例

日志:

1
2
3
4
5
6
7
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {

String value() default "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;

@Pointcut("@annotation(vip.infotech.common.annotation.SysLog)")
public void logPointCut() {

}

@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
//执行方法
Object result = point.proceed();
//执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;

//保存日志
saveSysLog(point, time);

return result;
}

private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

SysLogEntity sysLog = new SysLogEntity();
SysLog syslog = method.getAnnotation(SysLog.class);
if(syslog != null){
//注解上的描述
sysLog.setOperation(syslog.value());
}

//请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");

//请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = new Gson().toJson(args[0]);
sysLog.setParams(params);
}catch (Exception e){

}

//获取request
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
//设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));

//用户名
String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUserName();
sysLog.setUserName(username);

sysLog.setTime(time);
sysLog.setCreateTime(new Date());
//保存系统日志
sysLogService.insert(sysLog);
}
}

注解

  • Order 加载顺序调整
  • @Component注解默认实例化的对象是单例,如果想声明成多例对象可以使用@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)。还有Controller层也加@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)可以保证每个请求进来都创建新实例比较浪费资源。还可以通过实现BeanFactoryPostProcessor或者ApplicationContextAware的工具类手动创建进行控制SpringUtils.getBean("test");。通过@Component("abc")指定别名abc,按需创建实例。
  • @Repository默认单例
  • @Service默认单例
  • @Controller默认多例

@Transactional

ACID
你要往两张表甚至多张表中插入数据或者修改数据,或者说多次更新操作,那么场景上要求,这些数据都必须保证一致,要么全成功要么全失败,不能有中间数据产生,这样的话,肯定得用事务了。当然根据情况可能程序还需要加锁(数据库里面还有一些概念,数据库自带一些锁机制:比如引入隔离性、悲观锁、乐观锁 通过UNDO日志检测之前是否被修改、分布式系统更复杂了,为了避免死锁出现了两阶段锁、死锁检测等),当个事务可以保值原子性,但是多个事务同时执行可能原始值100一个事务给值+100另一个事务看到的值还是之前的值又给加100可能最终结果是200,也可能出现300。

抛出异常会回滚事务,手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();,在try catch 异常后事务不会被回滚可以通过该方法在catch中手动回滚,当然也可以catch处理后继续向上层抛出异常

1
2
3
4
5
6
7
8
9
10
11
try {

} catch (Exception e) {
throw new RuntimeException();
}

try {

} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}

单例与原型

单例对象实例注入多例对象实例时,由于单例对象在容器中只有一次初始化的机会,所以单例对象始终注入的都是同一个对象,这样不能满足我们需要多例的要求。可以手动new,也可以通过ApplicationContextAware或者applicationContext或者BeanFactoryPostProcessor ConfigurableListableBeanFactory组合做个工具类等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}

/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
}

ABCClient abcClient = SpringUtils.getBean("abc");

/* 实现原理:
通过beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false),获取spring配置文件中定义的所有实现BeanFactoryPostProcessor接口的bean,然后根据优先级进行排序,之后对于每个BeanFactoryPostProcessor,调用postProcessBeanFactory方法。
也就是程序启动的时候就会执行。可以通过断点postProcessBeanFactory查看具体调用链路:
postProcessBeanFactory:37, SpringUtils (vip.infotech.module.common.util)
invokeBeanFactoryPostProcessors:291, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:182, PostProcessorRegistrationDelegate (org.springframework.context.support) // beanFactory.getBeanNamesForType的位置
invokeBeanFactoryPostProcessors:707, AbstractApplicationContext (org.springframework.context.support)
refresh:533, AbstractApplicationContext (org.springframework.context.support) // 获取beanFactory,调用围绕beanFactory相关方法比如invokeBeanFactoryPostProcessors
refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:758, SpringApplication (org.springframework.boot)
refresh:750, SpringApplication (org.springframework.boot)
refreshContext:405, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1237, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
main:34, TestApplication (vip.infotech.module)
*/
// 还可以使用ApplicationContextAware

SpringMVC的controller默认就是单例的。如果加上@Scope("prototype")请求到Controller层的时候@Autowired的对象会每次创建否则都是用同一个。
如果你bean配置多例了,依赖这个bean的bean也需要多例才行
在单例的bean中切记声明成员属性(如Map、List集合来缓存数据),是线程不安全的,得用线程安全的ConcurrentHashMap

一个单例模式创建的对象是可以同时被多个线程处理的,如果一个对象被多个线程同时处理的话,很有可能出现线程同步问题,如果两个线程同时访问一个函数的话,要不要加锁呢,加锁怎么加,不加又怎样?一个单例模式的方法可以同时被多个线程处理,多个线程如果不是同时处理一个对象的共有属性,则不会出现线程问题,即使是方法中的属性如果两个线程同时访问同一个方法的时候,如果这个方法中没有共有的属性,则不需要加锁,反之则需要加锁。

Java特性使用

ThreadLocal

RequestContextHolder 保存请求参数,request。这样程序中可以获取请求参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class RequestContextHolder  {

private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");

private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
}

// 获取request的语句
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

在spring事务管理过程中会用到一些线程安全对象,这些对象都交由TransactionSynchronizationManager管理,TransactionSynchronizationManager把这些对象都保存在ThreadLocal中。

  1. 执行public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) DataSource加入TheadLocal
  2. 执行public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) 根据sessionFactory从ThreadLocal获取Session获取不到创建session()还是适用刚刚的DataSource创建,也就是配置文件配置的DataSource里面可以有多个数据源会有默认数据源具体看源码,能获取到直接用ThreadLocal获取到的SqlSession
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
...
try {
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
...
}
/**
* Start a new transaction.
*/
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}

public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); // ThreadLocal中获取
txObject.setConnectionHolder(conHolder, false);
return txObject;
}

protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.getConnectionHolder().setTransactionActive(true); // 就是放在ThreadLocal中。

// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); // 新建的事务绑定到ThreadLocal
}
}
}
public abstract class TransactionSynchronizationManager {

private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);

private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");

private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");

private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");

private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");

private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");

private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");

/**
* Retrieve a resource for the given key that is bound to the current thread.
* @param key the key to check (usually the resource factory)
* @return a value bound to the current thread (usually the active
* resource object), or {@code null} if none
* @see ResourceTransactionManager#getResourceFactory()
*/
@Nullable
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
@Nullable
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
Object value = map.get(actualKey);
// Transparently remove ResourceHolder that was marked as void...
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
// Remove entire ThreadLocal if empty...
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
/**
* Bind the given resource for the given key to the current thread.
* @param key the key to bind the value to (usually the resource factory)
* @param value the value to bind (usually the active resource object)
* @throws IllegalStateException if there is already a value bound to the thread
* @see ResourceTransactionManager#getResourceFactory()
*/
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
if (logger.isTraceEnabled()) {
logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
Thread.currentThread().getName() + "]");
}
}

}

public final class SqlSessionUtils {
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); // 真正执行insert语句的时候 从ThreadLocal中获取Resource

SqlSession session = sessionHolder(executorType, holder);

if (session != null) {
return session;
}

LOGGER.debug(() -> "Creating a new SqlSession");
session = sessionFactory.openSession(executorType); // 获取不到则创建

registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

return session;
}
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
SqlSessionHolder holder;
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();

if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holder); // 绑定到ThreadLocal
TransactionSynchronizationManager
.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); // 注册到ThreadLocal
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
LOGGER.debug(() -> "SqlSession [" + session
+ "] was not registered for synchronization because DataSource is not transactional");
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
LOGGER.debug(() -> "SqlSession [" + session
+ "] was not registered for synchronization because synchronization is not active");
}

}
}

public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment(); // 获取到的DataSource是早前放进去的。也就是配置文件中配置的DataSource
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}

多数据源切换,注解获取到的数据源名称可以存在线程ThreadLocal中。

部分常用类说明

Assert

进行参数校验,在运行时识别程序错误。会抛出异常,外层还可以根据需要进行try catch操作,也可以不处理后期根据日志分析报错原因。

1
2
3
4
Assert.notNull(clazz, "The class must not be null");
Assert.isTrue(i > 0, "The value must be greater than zero");
String a = "null";
Assert.notNull(a, "this argument is required;it must not be null");

SpringBoot

什么是springboot?

它是一个服务于spring框架的框架,能够简化配置文件,快速构建web应用,内置tomcat,无需打包部署,直接运行。

约定优于配置指的是什么?

maven 的目录结构
a) 默认有 resources 文件夹存放配置文件
b) 默认打包方式为 jar
默认提供 application.properties/yml 文件
默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件
EnableAutoConfiguration 默认对于依赖的 starter 进行自动

@SpringBootApplication由哪几个注解组成,这几个注解分别表示什么作用

SpringBootApplication 本质上是由 3 个注解组成,分别是

@Configuration
@EnableAutoConfiguration
@ComponentScan

@Configuration //在启动类里面标注了@Configuration,意味着它其实也是一个 IoC 容器的配置类

@EnableAutoConfiguration:
springboot 应用把所有符合条件的@Configuration 配置
都加载到当前 SpringBoot 创建并使用的 IoC 容器中。

@ComponentScan:
ComponentScan 默认会扫描当前 package 下的的所有加了 @Component 、@Repository、@Service、@Controller的类到 IoC 容器中

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

@Import注解的作用

@Import注解是用来导入配置类或者一些需要前置加载的类

可以是普通java类,可以是加了@Configuration注解的,也可以是ImportBeanDefinitionRegistrar

自动配置,AutoConfigurationImportSelector执行过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
}
}

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()); // 获取所有Configuration配置类
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
}
/**
调用链路:
getCandidateConfigurations:178, AutoConfigurationImportSelector (org.springframework.boot.autoconfigure)
getAutoConfigurationEntry:123, AutoConfigurationImportSelector (org.springframework.boot.autoconfigure) // 移除排除的配置
process:434, AutoConfigurationImportSelector$AutoConfigurationGroup (org.springframework.boot.autoconfigure)
getImports:879, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
processGroupImports:809, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
process:780, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
parse:193, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:319, ConfigurationClassPostProcessor (org.springframework.context.annotation) //处理配置bean
postProcessBeanDefinitionRegistry:236, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:280, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:96, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:707, AbstractApplicationContext (org.springframework.context.support)
refresh:533, AbstractApplicationContext (org.springframework.context.support)
refresh:143, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:758, SpringApplication (org.springframework.boot)
refresh:750, SpringApplication (org.springframework.boot)
refreshContext:405, SpringApplication (org.springframework.boot)
run:315, SpringApplication (org.springframework.boot)
run:1237, SpringApplication (org.springframework.boot)
run:1226, SpringApplication (org.springframework.boot)
main:34, TestApplication (vip.infotech.module)
*/

依赖一个spring-boot-starter-data-redis
的jar包,jar中定义了RedisConfiguration,当启动的时候spring会自动装载RedisConfiguration,那spring是如何知道配置类在哪里的呢?RedisConfiguration类的路径放在了classpath*META-INF/spring.factories的文件中,spring会加载这个文件中配置的configuration。

MyBatisPlus的spring.factories相关配置

1
2
3
4
5
6
# Auto Configure
org.springframework.boot.env.EnvironmentPostProcessor=\
com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

@Configuration原理

@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。用@Configuration加载spring,组合多个配置类。
注意:@Configuration注解的配置类有如下要求:

@Configuration不可以是final类型;
@Configuration不可以是匿名类;
嵌套的configuration必须是静态类。
一、用@Configuration加载spring
1.1、@Configuration配置spring并启动spring容器
1.2、@Configuration启动容器+@Bean注册Bean
1.3、@Configuration启动容器+@Component注册Bean
1.4、使用 AnnotationConfigApplicationContext 注册 AppContext 类的两种方法
1.5、配置Web应用程序(web.xml中配置AnnotationConfigApplicationContext)

二、组合多个配置类
2.1、在@configuration中引入spring的xml配置文件
2.2、在@configuration中引入其它注解配置
2.3、@configuration嵌套(嵌套的Configuration必须是静态类)
三、@EnableXXX注解
四、@Profile逻辑组配置
五、使用外部变量

@Configuration注解到底背后的工作原理是什么呢,笔者将从源码角度引入讲述@Configuration注解的工作原理,下面简单概述下Configuration注解是被初始化的流程:

AbstractApplicationContext::refresh–>AbstractApplicationContext::invokeBeanFactoryPostProcessors
–>ConfigurationClassPostProcessor::postProcessBeanFactory–>ConfigurationClassPostProcessor::enhanceConfigurationClasses

ConfigurationClassPostProcessor::enhanceConfigurationClasses这个方法是Configuration注解工作的核心方法,spring应用启动时所有的被@Configuration注解的类都会被spring cglib库生成cglib动态代理,然后其他地方通过@Autowired注解引入Student类对象就会被生成的configuration配置类生成的动态代理拦截处理完后再调用原configuration注解类的方法实例。

加上@Configuration注解主要是给我们的类加上了cglib代理。在执行我们的配置类的方法时,会执行cglib代理类中的方法,其中有一个非常重要的判断,当我们的执行方法和我们的调用方法是同一个方法时,会执行父类的方法new(cglib代理基于继承);当执行方法和调用方法不是同一个方法时会调用beanFactory.getBean获取。

在@Configuration中定义@Bean,Bean初始化的时候也就是相关功能模块进行初始化了(Bean里面还可以调用new xxx手动创建对象的方法),实现了配置模块的加载

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@AutoConfigureAfter(FileController.class)
public class TestConfiguration {
@Bean
public MyTestBean myBean () {
return new MyTestBean();
}
}
public class MyTestBean {
public MyTestBean() {
System.out.println("abc");
}
}

@SpringBootApplication中包含@EnableAutoConfiguration,会自动加载 声明@SpringBootApplication的类的package及其子package下的@Configuration类,创建Bean,其他的不加载,如果是内部工程可以调整路径层级。所以这样对于第三方包还需要spring.factories中配置。

可能遇到问题

注入对象为空

可能是加载顺序导致的,可以用Order注解或者调整xml配置顺序。或者对象增加构造函数,在存在多层调用的情况在外层通过构造函数把对象传递进去。

参考