1.IOC:反转控制
1)DI:依赖注入
2)IOC在spring容器的实现:
a).通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化
b)Spring提供了IOC容器的两种实现方式.
BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
3)ApplicationContext有两个主要实现类:
a) ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
b) FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
4)bean:
同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。
a)通过setXXX()方式赋值:
XML配置
<!-- 使用bean元素定义一个由IOC容器创建的对象 -->
<!-- class属性指定用于创建bean的全类名 -->
<!-- id属性指定用于引用bean实例的标识 -->
<bean id="student" class="first.bean.Student">
<!-- 使用property子元素为bean的属性赋值 -->
<property name="studentId" value="1001"/>
<property name="stuName" value="Tom2015"/>
<property name="age" value="20"/>
</bean>
代码实现:
//1创建IOC容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
//2根据id值得到bean实例
Student student = (Student) classPathXmlApplicationContext.getBean("student");
System.out.println(student);
b)通过构造的方式赋值
XML
<bean id="student1" class="first.bean.Student">
<constructor-arg value="007" index="0" type="java.lang.Integer"></constructor-arg>
<constructor-arg value="名字" index="1" type="java.lang.String"></constructor-arg>
<constructor-arg value="18" index="2" type="java.lang.Integer"></constructor-arg>
</bean>
<bean id="student11" class="first.bean.Student">
<constructor-arg name="studentId" value="007" ></constructor-arg>
<constructor-arg value="名字" name="stuName"></constructor-arg>
<constructor-arg value="18" name="age"></constructor-arg>
</bean>
代码实现:
@Test
public void test(){
//1创建IOC容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
//2根据id值得到bean实例
Student student = (Student) classPathXmlApplicationContext.getBean("student1");
System.out.println(student);
//2根据id值得到bean实例
Student student11 = (Student) classPathXmlApplicationContext.getBean("student11");
System.out.println(student11);
}
c)bean给属性赋null值,ref标签赋值以及属性里面有对象,集合,map,properties等属性;
xml
<bean id="student2" class="first.bean.Student">
<!-- 使用property子元素为bean的属性赋值 -->
<property name="stuName">
<null></null>
</property>
<!--list集合里面存一个对象-->
<!--对象实体-->
<property name="dog">
<bean class="first.bean.Dog">
<property name="name" value="大黄"></property>
</bean>
</property>
<property name="bookList">
<list>
<bean class="first.bean.Session">
<constructor-arg name="classId" value="001"></constructor-arg>
<constructor-arg name="className" value="计算机"></constructor-arg>
</bean>
<bean class="first.bean.Session">
<constructor-arg name="classId" value="002"></constructor-arg>
<constructor-arg name="className" value="高数"></constructor-arg>
</bean>
</list>
</property>
<!--map-->
<property name="map">
<map>
<entry>
<key>
<value>
map01
</value>
</key>
<bean class="first.bean.Dog">
<property name="name" value="my dog"></property>
</bean>
</entry>
<entry>
<key>
<value>
map02
</value>
</key>
<ref bean="secondog"></ref>
</entry>
</map>
</property>
<!--properties-->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
<bean id="secondog" class="first.bean.Dog">
<property name="name" value="大黄"></property>
</bean>
代码:
@Test
public void test01(){
//1创建IOC容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
//2根据id值得到bean实例
Student student = (Student) classPathXmlApplicationContext.getBean("student2");
System.out.println(student);
}
输出:
d)级联赋值:为对象的对象的属性赋值
xml
<!--级联赋值-->
<bean id="student3" class="first.bean.Student">
<property name="dog" ref="secondog">
</property>
<property name="dog.name" value="新的大黄"></property>
</bean>
e)通过继承实现bean配置信息的重用
(指的是配置信息的继承,并非类的继承)
加了一个parent的属性;
当加了一个abstract的属性的时候,就意味着只能继承,不能被调用获取数据
<bean id="student4" class="first.bean.Student" parent="student2">
<property name="dog" ref="secondog">
</property>
<property name="dog.name" value="新的大黄吗"></property>
</bean>
f)bean的创建顺序默认就是配置文件的配置顺序
,可以通过depends-on改变创建顺序
意思是等bean容器secondog,student3执行完成再执行student1
<bean id="student1" class="first.bean.Student" depends-on="secondog,student3">
<constructor-arg value="007" index="0" type="java.lang.Integer"></constructor-arg>
<constructor-arg value="名字" index="1" type="java.lang.String"></constructor-arg>
<constructor-arg value="18" index="2" type="java.lang.Integer"></constructor-arg>
</bean>
f) bean的作用域,指定bean是否单实例,默认是单实例
scope属性:有四种值:
prototype:多实例:当获取bean实例时候才会被创建,且每一次获取将得到一个新的实例。
singleton:单实例,单实例在容器启动完成就已经创建好对象,保存在容器中,构造器只调一次。
request:在web环境同一次请求创建一个bean实例(通常没用)
session:在web环境同一次会话创建一个bean实例(通常没用)
g)配置通过静态工厂方法创建bean,实例工厂创建bean、FactoryBean
bean的创建默认就是框架通过反射new出来的bean实例,
工厂模式:工厂帮我们创建对象
静态工厂:工厂本身不用创建对象,通过静态方法调用,对象=工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象,工厂类 对象 = new 工厂类,工程类.getAirPlane();
静态工厂XML:
这里并非要创建AirPlaneStaitcFactory实例,所以指定factory-method属性表名是个工厂,返回的不是工厂的对象而是该方法的对象
<!--这里并非要创建AirPlaneStaitcFactory实例,所以指定factory-method属性表名是个工厂,返回的不是工厂的对象而是该方法的对象-->
<bean id="staitcAirPlane" class="first.bean.AirPlaneStaitcFactory" factory-method="getAirPlane">
<constructor-arg name="name" value="歼20">
</constructor-arg>
</bean>
java代码:
package first.bean;
public class AirPlaneStaitcFactory {
public static AirPlane getAirPlane(String name){
AirPlane airPlane = new AirPlane();
airPlane.setName(name);
airPlane.setDriverName("机长名字");
airPlane.setLength("100m");
return airPlane;
}
}
实例工厂:
xml:
ii)先创建InstancePlaneFactory bean实例
ii)配置我们要创建的AirPlane使用哪个工厂
factory-bean指定哪个工厂实例
factory-method使用哪个工厂方法
<!--先创建实例工厂-->
<bean id="InstancePlaneFactory" class="first.bean.AirPlaneInstanceFactory">
</bean>
<bean id="InstancePlane" class="first.bean.AirPlane"
factory-bean="InstancePlaneFactory"
factory-method="getAirPlane">
<constructor-arg name="name" value="实例飞机"></constructor-arg>
</bean>
java
public void test2(){
//1创建IOC容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");
AirPlaneInstanceFactory airPlaneInstanceFactory = (AirPlaneInstanceFactory)classPathXmlApplicationContext.getBean("InstancePlaneFactory");
AirPlane AirPlane = airPlaneInstanceFactory.getAirPlane("实例机长");
System.out.println(AirPlane);
}
FactoryBean*(是spring规定的接口)
这个接口的实现类都是工厂类
xml:
<bean id="myFactoryBeanImple" class="first.bean.MyFactoryBeanImple">
</bean>
实现类:
getObject相当于工厂方法;
getObjectType返回工厂类型
isSingleton:是否单例;false不是单例
package first.bean;
import org.springframework.beans.factory.FactoryBean;
public class MyFactoryBeanImple implements FactoryBean<AirPlane>{
/**\
* 返回创建的对象
* @return
* @throws Exception
*/
@Override
public AirPlane getObject() throws Exception {
System.out.println("进来getObject方法。。。");
AirPlane airPlane = new AirPlane();
airPlane.setName("自带工厂方法的名字");
return airPlane;
}
/**
* 返回对象的类型
* spring会自带调用,来确定创建对象的类型
* @return
*/
@Override
public Class<?> getObjectType() {
System.out.println("进来getObjectType方法" +
"。。。");
return null;
}
/**
* false为单例
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
测试结果:
因为该工厂是spring认识的工厂,当容器一被创建的时候,
当isSingleton()方法返回false的时候,为多例,
当isSingleton()方法返回值为true时候,为单例子,;
不管是单实例还是多实例,只有bean被调用才创建。
h)创建带有生命周期方法的bean
生命周期:bean的创建到销毁
ioc容器中注册bean:
ii)、单例bean,容器启动的时候创建好,容器关闭时候销毁;
ii)多例bean,获取的时候才创建;
可以为bean自定义一些生命周期方法,在创建和销毁就可以自动调用;
bean里面可以指定destroy-method(销毁),和init-method(创建)属性
i)bean的后置处理器BeanPostProcessor,在bean的初始化前后调用方法
xml:
<bean id="airPlane" class="first.bean.AirPlane" init-method="myInit" destroy-method="myDestroy">
<property name="name" value="歼击机"></property>
</bean>
<bean id="processor" class="first.bean.MyBeanPostProcessor">
</bean>
java:
@Test
public void test3(){
//1创建IOC容器
ConfigurableApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean2.xml");
classPathXmlApplicationContext.close();
// System.out.println(AirPlane);
}
运行结果:
before和after方法分别在初始化之前和后初始化之后调用
执行顺序:
构造器—》后置处理器before—》初始化方法–》后置处理器after–》初始化完成
无论bean是否有初始化方法,后置处理器都会默认有,还有继续工作
j)引用外部属性文件
数据库连接池作为单实例是最好的,一个项目就一个连接池,连接池里面有很多连接,可以让spring帮忙创建
xml
方式一:直接在xml配置;
方式二:引入外部的配置文件
<context:property-placeholder location=“classpath:dbconfig.properties”/>
并加上命名空间:
xmlns:context="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
,为了防止和spring的属性重复,通常在配置的时候,加个前缀。eg:jdbc.username
<?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: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/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="hhh"></property>
<property name="jdbcUrl" value="jdbc:mysql://192.168.10.**:3306/xx?useUnicode=true&characterEncoding=utf8"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="processor" class="first.bean.MyBeanPostProcessor">
</bean>
<!--加载外部配置文件-->
<context:property-placeholder location="classpath:dbconfig.properties"/>
<bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
</beans>
java可以根据类型拿或者根据id去拿取;
根据类型拿到连接池,可以获得该类型的所有实现子类等等
@Test
public void test4(){
//1创建IOC容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
//拿到连接池
DataSource dataSource = (DataSource)classPathXmlApplicationContext.getBean("dataSource");
//根据类型拿到连接池,可以获得该类型的所有实现子类等等
DataSource dataSource11 = (DataSource)classPathXmlApplicationContext.getBean(DataSource.class);
DataSource dataSource2 = (DataSource)classPathXmlApplicationContext.getBean("dataSource2");
}
k)基于xml的自动装配
autowire=“defaut/no”
三种规则自动装配:
在bean中加入autowire的属性;
例如:
有参数构造器:
L)SPEL
#{1*12}//可以运算
#{book.name}//可以引用其他bean的属性或者直接引用其他bean
#(T(全类名).(静态方法))//调用静态方法
#{book1.getBookName()}//调用非静态 对象.方法名
M)使用注解快速加入容器的几步:
ii)加上controller,service,repository,component
ii)加上spring,自动扫描组件,依赖context名称空间
ii)id默认是类名首字母小写,(记得需要导入aop包)
xml
<context:component-scan base-package="first.controller">
</context:component-scan>
java
(也可以修改默认名称)
@Controller
//@Controller("hh")
public class bookController {
public void getBook(){
System.out.println("getBook");
}
}
N)@Autowired的原理:
ii)先按照类型去找,找到一个就赋值
ii)没找到抛出异常
ii)找到如果有多个就按照id去找
@Qualifier指定变量名称进行找
2.AOP
面向切面编程:将某段代码动态切入到方法指定位置运行的这种编程方式,面向切面编程;
1)前奏:使用jdk默认的动态代理的方式写入日志:
代理类:
代理对象传入实现接口,是为了让代理对象也实现与之相同的接口;使之能够调用相关的方法:Calculator proxy = CalculatorProxy.getProxy(calculator);
所以有个缺点:如果没有实现接口的话是没有办法使用jdk默认的动态代理;
package AOP;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CalculatorProxy {
/**
* 为传入的参数创建一个动态代理对象
* @param calculator
* @return
*/
public static Calculator getProxy(Calculator calculator) {
//方法执行器,帮我们目标对象执行目标方法
InvocationHandler h = new InvocationHandler() {
/**
*
* @param proxy:代理对象,给jdk使用,任何时候都不要动这个对象
* @param method:当前要执行的目标对象的方法
* @param args:这个方法调用时候,外界的参入的参数值
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("目标方法执行之前。。。");
//目标方法执行后的返回值
Object invoke = method.invoke(calculator,args);
System.out.println("目标方法执行之后。。。");
return invoke;
}
};
//实现了哪些接口
Class<?>[] interfaces = calculator.getClass().getInterfaces();
//被代理对象的类加载器
ClassLoader loader = calculator.getClass().getClassLoader();
//proxy为目标对象创建代理对象
Object proxy = Proxy.newProxyInstance(loader,interfaces,h);
return (Calculator) proxy;
}
}
CalculatorImpl类
package AOP;
public class CalculatorImpl implements Calculator {
@Override
public double add(double a, double b) {
System.out.println("加法运算结果:"+(a+b));
return a+b;
}
}
test类
package AOP;
import org.junit.Test;
public class AOPTest {
@Test
public void test(){
Calculator calculator = new CalculatorImpl();
calculator.add(1,1);
Calculator proxy = CalculatorProxy.getProxy(calculator);
proxy.add(3,1);
}
}
2)spring的aop简单使用(aop底层是动态代理)(cglib)
//Cglib动态代理,实现MethodInterceptor接口
//重写拦截方法intercept
面向切面编程的专业术语:
ii)导包
ii)写配置:
将目标和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中;
切面类
加上注解:
@Aspect
@Component
package Spring_AOP;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect
@Component
public class LogUtils {
/**
* try{
* @before
* method.invoke(obj,agrs);
* @AfterReturning
* }catch(e){
* @AfterThrowing
* }finally{
* @After
* }
* before目标方法执行之前 前置通知
* after目标方法结束之后 后置通知
* AfterReturn 在目标方法返回之后 返回通知
* @AfterThrowing在目标方法抛出异常之后 异常通知
* @Around 环绕通知
*/
//目标方法执行之前,写入切入点表达式
//execution(返回权限符 返回值类型 方法签名)
@Before("execution( public double Spring_AOP.Calculator.add(double ,double ))")
public static void logStart(){
System.out.println("开始执行");
// System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
}
//目标方法出现异常时候执行
@AfterThrowing("execution( public double Spring_AOP.Calculator.*(double ,double ))")
public static void logException(){
System.out.println("抛出异常之后");
// System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
}
//
@AfterReturning("execution( public double Spring_AOP.Calculator.*(double ,double ))")
public static void logReturn(){
System.out.println("正常执行之后");
// System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
}
@After("execution( public double Spring_AOP.Calculator.add(double ,double ))")
public static void logAfter(){
System.out.println("方法结束之后");
// System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
}
}
目标类的实现类
package Spring_AOP;
import org.springframework.stereotype.Component;
@Component
public class CalculatorImpl implements Calculator {
@Override
public double add(double a, double b) {
System.out.println("加法运算结果:"+(a+b));
return a+b;
}
@Override
public double ChuFa(double a, double b) {
return a/b;
}
}
还要告诉spring哪个是切面类@Aspect,何时执行
ii)在配置文件开启基于注解的aop功能
导入aop的空间名称以及注解扫描
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<context:component-scan base-package="Spring_AOP">
</context:component-scan>
<!--开辟基于注解的aop功能,aop名称空间-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
通知:
/**
* try{
* @before
* method.invoke(obj,agrs);
* @AfterReturning
* }catch(e){
* @AfterThrowing
* }finally{
* @After
* }
* before目标方法执行之前 前置通知
* after目标方法结束之后 后置通知
* AfterReturn 在目标方法返回之后 返回通知
* @AfterThrowing在目标方法抛出异常之后 异常通知
* @Around 环绕通知
*/
//目标方法执行之前,写入切入点表达式
//execution(返回权限符 返回值类型 方法签名)
@Before(“execution( public double Spring_AOP.Calculator.add(double ,double ))”)
测试类
@Test
public void test2(){
try{
Calculator calculator = (Calculator)ioc.getBean(Calculator.class);
// Calculator calculator = new CalculatorImpl();
// calculator.add(1,1);
// int i = 1/0;
calculator.ChuFa(1,0);
// int i = 1/0;
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
}
}
注:容器加在接口时候是不创建对象的(spring比较智能),所以通常是加载实现类上
3)切入点表达式的写法
ii)匹配一个或者多个字符
@Before(“execution( public double Spring_AOP.Calculator.*(double ,double ))”)
ii)匹配任意一个参数,第一个是int类型,第二个参数任意类型(匹配任意类型)
execution( public double Spring_AOP.Calculator.(int ,))
ii)匹配任意多个参数,任意类型参数
execution( public double Spring_AOP.Calculator.(…))
ii)匹配任意多次路径
execution( public double Spring_AOP1…Spring_AOP12Calculator.(…))
ii)权限位置不能代表任意,权限位置不写就表示任意;
execution( double Spring_AOP1…Spring_AOP12Calculator.(…))
ii)全权限,任意返回类型,任意路径,任意参数
execution( * *(…))
4)在通知方法运行的时候,拿到目标方法的详细信息
只需要在通知方法的参数列表写上一个参数
JoinPoint joinPoint
@Before("execution( public double Spring_AOP.Calculator.add(double ,double ))")
public static void logStart(JoinPoint joinPoint){
System.out.println("开始执行");
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:"+Arrays.toString(args));
// System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
}
5)在通知方法运行的时候,拿到目标方法的详细信息,(拿到返回值,异常)
ii)返回值
当Object result改为double result的时候,就只能接收浮点类型
@AfterReturning(value="execution( public double Spring_AOP.Calculator.*(double ,double ))",returning = "result")
public static void logReturn(JoinPoint joinPoint,Object result){
System.out.println("正常执行之后");
System.out.println("result:"+result);
// System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
}
ii)异常信息
//目标方法出现异常时候执行
@AfterThrowing(value = "execution( public double Spring_AOP.Calculator.*(double ,double ))",throwing ="exception" )
public static void logException(JoinPoint joinPoint,Exception exception){
System.out.println("抛出异常之后");
// System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
}
6)也可以把表达式抽取出来,实现可重入,
@Pointcut(“execution( public double Spring_AOP.Calculator.add(double ,double ))”)
@Pointcut("execution( public double Spring_AOP.Calculator.add(double ,double ))")
public void hahaaMyPoint(){
}
//目标方法执行之前,写入切入点表达式
//execution(返回权限符 返回值类型 方法签名)
@Before(value = "hahaaMyPoint()")
public static void logStart(JoinPoint joinPoint){
System.out.println("开始执行");
Object[] args = joinPoint.getArgs();
System.out.println("方法参数:"+Arrays.toString(args));
// System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
}
7)环绕通知:
执行顺序:
环绕通知优先于其他普通通知
环绕前置–》环绕后置—》方法返回–》环绕正常返回/方法异常–》环绕后置–》普通后置
环绕通知有个参数ProceedingJoinPoint ,环绕通知是“四合一”的通知
@Around("hahaaMyPoint()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object[] args = proceedingJoinPoint.getArgs();
Object proceed = null;
try {
System.out.println("myAround开始");
//利用反射执行目标方法
proceed = proceedingJoinPoint.proceed(args);
System.out.println("myAround正常执行结束");
}catch (Exception e){
System.out.println("myAround抛出异常");
}finally {
System.out.println("myAround最后");
}
return proceed;
}
注:当有多个切面的时候,默认按照字母文件的首字母的字母表顺序,也可以通过加上@Order(1)来改变顺序,数字越小,优先级越高
3基于xml的aop
1)加到ioc容器
2)需要名称空间,告诉哪个是切面类,以及相对应的通知方法
<bean id="calculator2" class="Spring_AOP.CalculatorImpl"></bean>
<bean id="logUtils" class="Spring_AOP.LogUtils"></bean>
<aop:config>
<!--指定切面-->
<aop:aspect ref="logUtils">
<aop:pointcut id="hahaaMyPoint2" expression="execution( public double Spring_AOP.Calculator.add(double ,double ))"/>
<!--相当于前置通知-->
<aop:before method="logStart2" pointcut="execution( public double Spring_AOP.Calculator.add(double ,double ))"/>
<!--exception是来接受异常的-->
<aop:after-throwing method="logException2" pointcut-ref="hahaaMyPoint2" throwing="exception"></aop:after-throwing>
</aop:aspect>
</aop:config>
4.JdbcTemplate
xml配置数据源:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="hhh"></property>
<property name="jdbcUrl" value="jdbc:mysql://192.168.10.**:3306/xx?useUnicode=true&characterEncoding=utf8"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
java代码:
5.声明式事务
四个关键特性:
原子性:要么都执行,要么都不执行
一致性;数据一致性,例如:我转一百,对方也将收到一百,不可能我转了1百对方没收到啥的;
隔离性;多事务并发执行互不干扰
持久性;对数据的修改永久保存下去。
事务切面===事务管理器
为某个方法添加事务管理器:
1)配置事务管理器让其进行事务控制,需要面向切面编程的包
2)开启基于注解的事务控制模式,依赖tx名称空间
3)给事务方法加注解
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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="TX">
</context:component-scan>
<!--加载外部配置文件-->
<context:property-placeholder location="classpath:dbconfig.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<!--事务控制-->
<!--1.事务管理器-->
<bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2开启基于注解的事务控制模式,依赖tx名称空间-->
<tx:annotation-driven transaction-manager="tm"/>
<!--3.给事务方法加上注解-->
</beans>
BookDao
package TX;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
@Repository
public class BookDao {
// @Autowired
@Resource(name = "jdbcTemplate")
JdbcTemplate jdbcTemplate;
@Test
public void test(){
System.out.println(jdbcTemplate);
String sql ="select price from book where isbn = 'ISBN-001'";
int price = jdbcTemplate.queryForObject(sql,Integer.class);
System.out.println(price);
}
void updateStock(String isbn){
String sql ="update book_stock set stock=stock-1 where isbn = ?";
jdbcTemplate.update(sql,isbn);
}
void updateAccount(String username,int balance){
String sql ="update account set balance=balance-? where username = ?";
jdbcTemplate.update(sql,balance,username);
}
int getPrice(String isbN){
String sql ="select price from book where isbn = 'ISBN-001'";
int price = jdbcTemplate.queryForObject(sql,Integer.class);
return price;
}
}
BookService
package TX;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BookService {
@Autowired
BookDao bookDao;
@Transactional
public void checkOut(String username,String isbn){
// 1减去库存
bookDao.updateStock(isbn);
int price = bookDao.getPrice(isbn);
int i=1/0;
//2减去余额
bookDao.updateAccount(username,price);
}
}
测试代码:
(采用Junit无法调用到注解的jdbcTemplate,这里采用spring自带的测试@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { “classpath:tx.xml” }))
package TX;
import Spring_AOP.Calculator;
import Spring_AOP.CalculatorImpl;
import Spring_AOP.CalculatorProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.sql.DataSource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:tx.xml" })
//@Component
public class JDBCTemplateTest {
//1创建IOC容器
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("tx.xml");
JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
@Autowired
private BookService bookService;
@Autowired
private BookDao bookDao;
@Test
public void test(){
// System.out.println(jdbcTemplate);
// String sql ="select price from book where isbn = 'ISBN-001'";
// int price = jdbcTemplate.queryForObject(sql,Integer.class);
// System.out.println(price);
// 1减去库存
bookService.checkOut("Tom","ISBN-001");
// int count= bookDao.getPrice("ISBN-001");
// System.out.println("数量:"+count);
}
}
6.事务细节
isolation:事务的隔离级别
propagation:事务的传播行为
timeOut:超时,自动终止并且回滚
readOnly:设置事务为只读事务,可以进行事务优化,为true
时候,加快查询速度,不用管事务一堆操作,默认是false
运行时异常:可以不处理,默认都回滚;
编译时异常:要么try-catch,要么在方法声明throw,默认不回滚
noRollbackFor:哪些异常可以不回滚(可以让默认回滚的异常不回滚)
noRollbackForName:
rollbackFor:哪些不回滚的异常可以回滚
rollbackForName:
1)隔离级别:
并发问题:
ii)脏读:(只有脏读是一定不能发生)
ii)不可重复读:(指的是更新的数据)
ii)幻读:(指的是插入的记录)
四种隔离级别:
(肯定是一个在读,一个在写才会出现这个问题)
四种隔离级别分别有什么问题:
2)模拟并发问题:
ii)默认隔离级别
SELECT @@tx_isolation;
REPEATABLE-READ
ii)修改为读取未提交的
set session transaction isolation level read uncommitted;(当前会话)
ii)模拟脏读
ii)模拟不可重复读
ii)可重复读:repeatable read
并发修改数据的时候,底层将会进行排队
3)传播行为
如果多个事务进行嵌套运行,子事务是否要和大事务共用一个事务
eg:@transactional(propagation=Propagation.REQUIRED,timeout=3)
当使用Propagation.REQUIRED,所有的子事务将继承于大事务
(REQUIRED和REQUIRED_NEW最常见)
情况分析
ii)当大事务里面嵌套了两个小事务
当大事务抛出异常的时候,第一个事务REQUIRED_NEW可以成功执行,第二个REQUIRED执行失败
ii)当第一个子事务发生异常时候,虽然传播行为是REQUIRED_NEW,但是产生的异常会往外抛,导致大事务也发生异常,并且第二个子事务是REQUIRED,所以第二个子事务也执行不成功
ii)当第二个事务发生异常时候,并不影响第一个成功执行
7.基于xml的事务控制配置
1)配置事务管理器
2)卡配置事务方法
3)告诉spring哪些是事务方法(事务切面按照我们切入表达式切入事务方法)
xml配置
<aop:config>
<aop:pointcut id="point" expression="execution(* TX(..))"/>
<!-- 将切入点表达式和事务属性配置关联到一起 -->
<aop:advisor advice-ref="mtTx" pointcut-ref="point"></aop:advisor>
</aop:config>
<!-- 配置基于XML的声明式事务 -->
<tx:advice id="mtTx" transaction-manager="tm">
<tx:attributes>
<!-- 设置具体方法的事务属性 -->
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="checkOut2"
isolation="READ_COMMITTED"
no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
propagation="REQUIRED"
read-only="false"
timeout="10"/>
</tx:attributes>
</tx:advice>
8.源码分析
//1创建IOC容器
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("bean.xml");
当执行这语句之后,所有的bean将创建并初始化完成。
1) AbstractApplicationContext有一个Refresh()实现单实例bean的创建
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
//解析xml配置文件,将bean的配置信息保存起来
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
//国际化功能
this.initMessageSource();
this.initApplicationEventMulticaster();
//空方法,留给子类
this.onRefresh();
this.registerListeners();
//初始化所有单实例bean
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
2)DefaultListableBeanFactory:preInstantiateSingletons()创建bean
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Pre-instantiating singletons in " + this);
}
//拿到所有需要创建的bean的名字
List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();
//按照顺序创建bean
while(true) {
String beanName;
Object bean;
do {
while(true) {
RootBeanDefinition bd;
do {
do {
do {
if (!var2.hasNext()) {
var2 = beanNames.iterator();
while(var2.hasNext()) {
beanName = (String)var2.next();
Object singletonInstance = this.getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, this.getAccessControlContext());
} else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
return;
}
beanName = (String)var2.next();
//根据beanId获取bean的定义信息
bd = this.getMergedLocalBeanDefinition(beanName);
} while(bd.isAbstract());
} while(!bd.isSingleton());
} while(bd.isLazyInit());
if (this.isFactoryBean(beanName)) {
bean = this.getBean("&" + beanName);
break;
}
this.getBean(beanName);
}
} while(!(bean instanceof FactoryBean));
FactoryBean<?> factory = (FactoryBean)bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
SmartFactoryBean var10000 = (SmartFactoryBean)factory;
((SmartFactoryBean)factory).getClass();
isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
} else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
}
if (isEagerInit) {
//创建对象
this.getBean(beanName);
}
}
}
3)AbstractBeanFactory:getBean()
所有getBean调用的是AbstractBeanFactory
的dogetBean()
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = this.transformedBeanName(name);
//试着从已经注册的所有单实例bean中看有没有这个bean,第一次创建bean是没有的
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if (sharedInstance != null && args == null) {
if (this.logger.isTraceEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
String nameToLookup = this.originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
if (args != null) {
return parentBeanFactory.getBean(nameToLookup, args);
}
if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
return parentBeanFactory.getBean(nameToLookup);
}
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMerge,dBeanDefinition(mbd, beanName, args);
//拿到所有依赖的bean,depends-on属性
String[] dependsOn = mbd.getDependsOn();
String[] var11;
if (dependsOn != null) {
var11 = dependsOn;
int var12 = dependsOn.length;
for(int var13 = 0; var13 < var12; ++var13) {
String dep = var11[var13];
if (this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
this.registerDependentBean(dep, beanName);
try {
this.getBean(dep);
} catch (NoSuchBeanDefinitionException var24) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
}
}
}
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
Scope scope = (Scope)this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
this.beforePrototypeCreation(beanName);
Object var4;
try {
var4 = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
return var4;
});
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var23) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);
}
}
} catch (BeansException var26) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var26;
}
}
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
} else {
return convertedBean;
}
} catch (TypeMismatchException var25) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
} else {
return bean;
}
}
4)getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
Map var4 = this.singletonObjects;
synchronized(this.singletonObjects) {
//创建对象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//创建好的对象最终保存在earlySingletonObjects中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}