Spring
Spring的启动过程
- 调用ContextLoader的初始化方法创建webApplicationContext上下文环境
- 加载Spring配置文件中的Bean对象【refresh方法】
- 将webApplicationContext放入ServletContext【java web全局变量】
反射
Reflection
反射机制允许程序执行期间获取类的任何内部信息。
反射的作用
在运行时可以知道任意一个类的所有属性,方法,并可以改变他的属性,应用在各种框架
反射的原理
java是一种编译型语言,在运行时,java文件先进行编译为class
这些编译好的class文件中包含类的属性方法信息,反射可以通过这些class文件来获取类的信息
通过不同方式获得的class对象是相同的。每个类只有一个class对象
实现反射的三种方式
- 通过路径
Class clazz = Class.forName("com.java.grandland.Old");
- 通过对象
Old o = new Old();
Class clazz = o.getClass();
- 通过类
Class clazz1 = Old.class
注解
java.lang.annotation.Annotation
Java中常见的注解
@Override:重载,修饰方法
@Deprecated:废弃的,修饰方法,属性,类
@SuppressWarnings:抑制警告
元注解
用来注解自定义的注解
@Target: 表示自定义注解可以用在哪些地方
@Retenion:表示自定义注解在什么地方还有效【RUNTIME,CLASS,SOURCE】
@Documented:表示是否将我们的注解生成在javadoc中
@Inherited:子类可以继承父类注解
自定义注解
使用@interface声明一个注解
使用@Target和@Retenion定义定义作用位置和时间
只有一个value参数时,使用时可以默认不写
依赖注入
再注入时使用了反射机制生成注入对象
三种方法
- setter方法
主要方式
构造方法声明为无参数的,使用setter注入为其设置对应的值
<bean id="role2" class="com.ssm.chapter9.pojo.Role">
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"/>
</bean>
- 构造函数
有参构造函数
<bean id="role1" class="com.ssm.chapter9.pojo.Role">
<constructor-arg index="0" value="总经理"/>
<constructor-arg index="1" value="公司管理者"/>
</bean>
-
接口
有些时候资源并非来自于自身系统,而是来自于外界,比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源,这个时候我们可以采用接口注入的形式来获取它
自动注入
- @Autowired
spring会在bean容器中寻找能够匹配的Bean,进行自动注入。如果存在多个能够匹配的bean就会出现问题。
默认按照对象类型进行自动注入。
自动注入的对象为空时,会抛出异常,使用required参数为false可以允许为null。 - @Qualifier
在自动注入时增加@Qualifier,用于缩小匹配范围,指定bean的名字
在bean上使用@Qualifier,用来进行标注,方便自动注入时引用。 - @Primary
在bean上增加@Primary,表示在冲突时首选改bean对象 - @Resource
默认按照bean的name进行自动注入,两个参数name和type。也可指定根据类型匹配。
循环依赖
在进行依赖注入时,相互引用会发生循环依赖,导致抛出异常。发生在构造器注入和prototype类型setter注入时。
原因:
-
因为spring使用构造器来实例化Bean对象,完成实例化之后设置对象属性,使用构造器注入方式会陷入死循环问题,改为使用setter单例注入方式,会先创建Baan对象,之后会以属性的方式注入其他Bean对象。
-
使用setter注入的prototype方式注入,同样会抛出异常,因为singleton单例作用域时,会在spring初始化时初始化Bean对象,而prototype作用域是懒加载模式,spring容器不进行缓存,在进行注入时无法找到需要的Bean对象。
解决方式:
- 使用setter注入的singleton模式
- 使用@Autowired自动注入
AOP
代理模式
动态代理的代理类是动态生成的,不是直接写好的。
分为两类:
- 基于接口 – JDK动态代理
- 基于类 – cglib
JDK动态代理使用方法:
-
实现InvocationHandle
-
使用Proxy创建代理类
-
实现invoke方法,对要代理的对象方法进行增强
class StarImpl implements Star{
@Override
public void sing() {
System.out.println("sing");
}
}
interface Star {
void sing();
}
class InvocationImpl implements InvocationHandler {
private Object target;
public InvocationImpl(Object target) {
this.target = target;
}
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("q3w2132");
Object o = null;
try{
o= method.invoke(target,args);
} catch(Exception e) {
e.printStackTrace();
}
return o;
}
public static void main(String[] args) {
Star o = (Star)(new InvocationImpl(new StarImpl()).getProxy());
o.sing();
}
}
CGLIB方式:
- 通过字节码技术动态创建子类
- 在子类中拦截父类方法的调用,织入横切逻辑
- 采用继承模式,不能对final修饰的类进行代理
区别:
- CGLIB创建动态代理对象比JDK性能更高,花费时间较长
- CGLIB适用于单例对象,无需频繁创建
- CGLIB无法对final修饰的类进行处理
spring中AOP的动态代理技术实现
如果目标对象实现了接口则使用JDK动态代理实现AOP
否则使用CGLIB
AOP
使用了代理模式
- JoinPoint(连接点)
目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。 - Pointcut(切入点)
目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。 - Advice(通知/增强)
增强方法的代码、想要的功能。 - Target(目标对象)
被代理对象,被通知的对象,被增强的类对象。 - Weaving(织入)
将通知应用到连接点形成切入点的过程 - Proxy(代理)
将通知织入到目标对象之后形成的代理对象 - aspect(切面)
切入点+ 通知。说明了干什么的内容和什么时候干。
几种通知类型
1.前置通知———目标方法运行之前调用
2.后置通知———目标方法运行之后调用(如果出现异常不调用)
3.环绕通知———目标方法之前和之后都调用
4.异常拦截通知———如果出现异常,就会调用
5.后置通知———目标方法运行之后调用(无论是否出现异常都会调用)
事务
事务的ACID原则,要么都成功,要么都失败
-
原子性
原子性操作,一个整体,要么都成功要么都失败
-
一致性
多次执行结果一致
-
隔离性
多个业务操作同一个资源
-
持久性
事务一旦提交,结果不会改变
spring中事务处理
- 声明式事务
- 编程式事务