Spring

一、Spring框架(容器框架):纵跨veb层(管理层),service(业务层),dao层(持久层),可以配置各个层的组件(bean)并且维护各个bean之间的关系的胶水框架

 二、Spring概念:接触耦合度 提高代码复用性 Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)核心技术IOC(DI)和AOP

三、Spring架构组成

  • 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP

  • 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。

  • 数据访问:事务,DAO支持,JDBC,ORM,封送XML。

  • Spring MVC和 Spring WebFlux Web框架。

  • 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。

  • 语言:Kotlin,Groovy,动态语言

 

 四、控制反转 IoC(Inversion of Control )

1. 控制反转:一种设计思想 是指将硬编码方式主动new一个对象的控制权转交给Spring容器进行管理 由Spring通过Java的反射机制根据配置文件在运行时 动态的创建实例 并管理各个实例之间的依赖关系 解决了具有依赖关系的组件之间的强耦合 由容器管理所有组件 使项目运行更加稳健 

 五、DI (Dependency Injection)依赖注入(构造器注入constructor-arg)

public class User {
    private Integer id;
    private String password;
    private String sex;
    private Integer age;
    private Date bornDate;
    private String[] hobbys;
    private Set<String> phones;
    private List<String> names;
    private Map<String,String> countries;
    private Properties files;
    //Getters And Setters
}
<bean id="u1" class="com.qf.spring.part1.injection.User">
    <!--base field-->
    <property name="id" value="1001" />
    <property name="password" value="123456" />
    <property name="sex" value="male" />
    <property name="age" value="20" />
    <property name="bornDate" value="1990/1/1" /><!--注意格式"/"-->
</bean>
<bean id="u1" class="com.qf.spring.part1.injection.User">	
	<!--Array-->
    <property name="hobbys">
        <array>
            <value>Run</value>
            <value>Swim</value>
            <value>Climb</value>
        </array>
    </property>

    <!--Set-->
    <property name="phones">
        <set>
            <value>13777777777</value>
            <value>13888888888</value>
            <value>13999999999</value>
        </set>
    </property>

    <!--List-->
    <property name="names">
        <list>
            <value>tom</value>
            <value>jack</value>
            <value>marry</value>
        </list>
    </property>

    <!--Map-->
    <property name="countries">
        <map>
            <entry key="CN" value="China" />
            <entry key="US" value="America" />
            <entry key="KR" value="Korea" />
        </map>
    </property>
    
    <!--Properties-->
    <property name="files">
        <props>
            <prop key="first">One</prop>
            <prop key="second">Two</prop>
            <prop key="third">Three</prop>
        </props>
    </property>
</bean>
<!--次要bean,被作为属性-->
<bean id="addr" class="com.qf.spring.part1.injection.Address">
    <property name="position" value="北京市海淀区" />
    <property name="zipCode" value="100001" />
</bean>

<!--主要bean,操作的主体-->
<bean id="u2" class="com.qf.spring.part1.injection.User">
    <property name="address" ref="addr" /><!--address属性引用addr对象-->
</bean>
<!--次要bean,被作为属性-->
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />

<!--主要bean,操作的主体-->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl">
    <property name="ud" ref="userDao" /><!--ud属性引用userDao对象-->
</bean>

自动注入:不用在配置中 指定为哪个属性赋值,及赋什么值.

由spring自动根据某个 "原则" ,在工厂中查找一个bean,为属性注入属性值

通过byType 缺点(容器中不能有相同类型对象) 或者 ByName 

public class UserServiceImpl implements UserService {
    private UserDAO userDAO;
    //Getters And Setters
    ....
}
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byType"></bean>
<bean id="userDao" class="com.qf.spring.part1.injection.UserDaoImpl" />
<!-- 为UserServiceImpl中的属性基于类型自动注入值 -->
<bean id="userService" class="com.qf.spring.part1.injection.UserServiceImpl" autowire="byName"></bean>

五、bean 细节

1.控制单例多例:配置< bean scope="singleton | prototype" /> 

2. 单例是创建容器时就创建对象 多例是getBean时创建对象

3.创建复杂对象:通过getBean得到connection

public class MyConnectionFactoryBean implements FactoryBean<Connection> {
    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }

    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/qf.edu?useSSL=false&useUnicode=true&characterEncoding=utf8","root","1234");
        return connection;
    }

    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }
}
<bean id="connection" class="com.qf.bean.MyConnectionFactoryBean"></bean>

六、Spring工厂特性

1.饿汉式创建:工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。

提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)

2.xml文件中 添加 init-method="intiMethod" destroy-method="destroyMethod" 

也可以通过注解 @PostConstruct //初始化  @PreDestroy //销毁

2.1 先调用构造器 然后调用初始化方法 父类ApplicationContext没有close方法

2.2 想调用初始化方法需要用子类子类ClassPathXmlApplicationContext 对象.close

2.3 ClassPathXmlApplicationContext.close方法被调用后自动注入的单例对象被销毁

2.4 在多例情况下 getBean时创建对象 多例模式下由JVM关闭

public void intiMethod(){
        System.out.println("初始化方法被调用");
    }
    public void destroyMethod(){
        System.out.println("销毁方法被调用");
    }
<bean id="student" class="com.qf.pojo.Student" init-method="intiMethod" destroy-method="destroyMethod" scope="prototype / singleton">

七、动态代理:将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用

1.静态代理设计模式:通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护

2. 代理模式:不更改核心功能的情况下 通过中介的方式提供辅助功能来实现需求 利于维护使用        

代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法

静态代理问题:代理类数量过多,不利于项目的管理,多个代理类的辅助功能代码冗余,修改时,维护性差

 2. 动态代理设计模式: 动态创建代理类的对象,为原始类的对象添加辅助功能

基于JDK实现

//目标
final OrderService os = new OrderServiceImpl();
//额外功能
InvocationHandler handler = new InvocationHandler(){//1.设置回调函数(额外功能代码)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        System.out.println("start...");
        method.invoke(os, args);
         System.out.println("end...");
        return null;
    }
};
//2.创建动态代理类
Object proxyObj = Proxy.newProxyInstance(ClassLoader , Interfaces , InvocationHandler);

//---------------------------------------------------------------------------------
FangDongService fangDongService = new FangDongServiceImpl();
InvocationHandler ih = new InvocationHandler(){
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        System.out.println("发布信息...");
        System.out.println("带租客看房...");
        fangDongService.zufang();
        return null;
    }
}
FangDongService proxy = (FangDongService)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),
fangDongService.getClass().getinterfaces(),ih);
proxy.zufang();
//首先有个“明星”接口类,有唱、跳两个功能
public interface Star
{
    String sing(String name);
    
    String dance(String name);
}
//再有个明星实现类“刘德华”
public class LiuDeHua implements Star
{   
    @Override
    public String sing(String name)
    {
         System.out.println("给我一杯忘情水");
 
        return "唱完" ;
    }
    
    @Override
    public String dance(String name)
    {
        System.out.println("开心的马骝");
 
        return "跳完" ;
    }
}
//明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务
public class StarProxy implements InvocationHandler
{
    // 目标类,也就是被代理对象
    private Object target;
    
    public void setTarget(Object target)
    {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        // 这里可以做增强
        System.out.println("收钱");
        
        Object result = method.invoke(target, args);
        
        return result;
    }
    
    // 生成代理类
    public Object CreatProxyedObj()
    {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }  
   
}

public static void main(String[] args){
    Star ldh = new LiuDeHua();
    StarProxy proxy = new StarProxy();
    proxy.setTarget(ldh); 
    Object obj = proxy.CreatProxyedObj();
    Star star = (Star)obj;
    star.sing("忘情水");
}

CGlib动态代理

final OrderService os = new OrderServiceImpl();
Enhancer cnh = new Enhancer();//1.创建字节码曾强对象
enh.setSuperclass(os.getClass());//2.设置父类(等价于实现原始类接口)
enh.setCallback(new InvocationHandler(){//3.设置回调函数(额外功能代码)
    @Override
    public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{
        System.out.println("start...");
        Object ret = method.invoke(os,args);
        System.out.println("end...");
        return ret;
    }
});
OrderService proxy = (OrderService)enh.create();//4.创建动态代理类
proxy,createOrder();

//--------------------------------------------------
FangDongService fangDongService = new FangDongServiceImpl();
Enhancer cnh = new Enhancer();
enh.setSuperclass(FangDongServiceImpl.class);
enh.setCallback(new InvocationHandler(){
    public Object invoke(Object proxy , Method method, Object[] args) throws Throwable{
        System.out.println("start...");
        fangDongService.zufang();
        System.out.println("end...");
        return null;
    }
});
FangDongServiceImpl proxy = (FangDongServiceImpl)cnh.create();//4.创建动态代理类
proxy.zufang();
public class CglibProxy implements MethodInterceptor
{
    // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
    public Object CreatProxyedObj(Class<?> clazz)
    {
        Enhancer enhancer = new Enhancer();
        
        enhancer.setSuperclass(clazz);
        
        enhancer.setCallback(this);
        
        return enhancer.create();
    }
    
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
    {
        // 这里增强
        System.out.println("收钱");
        
        return arg3.invokeSuper(arg0, arg2);
    } 
}
//测试
public static void main(String[] args)
{
    int times = 1000000;

    Star ldh = new LiuDeHua();
    StarProxy proxy = new StarProxy();
    proxy.setTarget(ldh);

    long time1 = System.currentTimeMillis();
    Star star = (Star)proxy.CreatProxyedObj();
    long time2 = System.currentTimeMillis();
    System.out.println("jdk创建时间:" + (time2 - time1));

    CglibProxy proxy2 = new CglibProxy();
    long time5 = System.currentTimeMillis();
    Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
    long time6 = System.currentTimeMillis();
    System.out.println("cglib创建时间:" + (time6 - time5));

    long time3 = System.currentTimeMillis();
    for (int i = 1; i <= times; i++)
    {
        star.sing("ss");

        star.dance("ss");
    }
    long time4 = System.currentTimeMillis();
    System.out.println("jdk执行时间" + (time4 - time3));

    long time7 = System.currentTimeMillis();
    for (int i = 1; i <= times; i++)
    {
        star2.sing("ss");

        star2.dance("ss");
    }

    long time8 = System.currentTimeMillis();

    System.out.println("cglib执行时间" + (time8 - time7));   
}
//经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib

 八、面向切面编程【重点

1.AOP(Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性

2.AOP开发术语:

  • 连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。

  • 切入点(Pointcut):被Spring切入连接点。

  • 通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。

  • 目标对象(Target):代理的目标对象

  • 引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。

  • 织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。

  • 代理(Proxy):被AOP织入通知后,产生的结果类。

  • 切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中

 3. 作用:Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能

 4.环境依赖:配置在applicationContext中

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
public interface UserService {
    List<User> queryUser();
    Integer saveUser(User user);
    Integer updateUser(User user);
    Integer deleteUser(Integer id);

}
public class UserServiceImpl implements UserService{
    @Override
    public List<User> queryUser() {
        System.out.println("queryUser.... ");
        List list = new ArrayList();
        list.add(new User(1001,"tom","123"));
        list.add(new User(1002,"lucky","777"));
        return list;
    }

    @Override
    public Integer saveUser(User user) {
        System.out.println("saveUser....");
        return 1;
    }

    @Override
    public Integer updateUser(User user) {
        System.out.println("updateUser....");
        //int a = 10/0;
        return 1;
    }

    @Override
    public Integer deleteUser(Integer id) {
        System.out.println("deleteUser....");
        return 1;
    }
}
public class MyAdviceAfter implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知被调用");
    }
}
public class MyAdviceException implements ThrowsAdvice {
     public void afterThrowing(Exception e) throws Throwable{
         System.out.println("after exception...");
     }
}
public class MyAdviceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("begin");
        Object rest = invocation.proceed();
        System.out.println("end");
        return rest;
    }
}
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("发布信息");
        System.out.println("带客户看房");
    }
}
public class User {
    private Integer id;
    private String name;
    private String password;
    
    set...
    get...
    toString...
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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.xsd
       ">
<!--    创建userService对象-->
    <bean id="userService" class="com.qf.service.UserServiceImpl"/>
    <bean id="myBeforeAdvice" class="com.qf.advice.MyBeforeAdvice"/>
    <bean id="myAdviceAfter" class="com.qf.advice.MyAdviceAfter"/>
    <bean id="myAdviceException" class="com.qf.advice.MyAdviceException"/>
    <bean id="myAdviceMethodInterceptor" class="com.qf.advice.MyAdviceMethodInterceptor"/>

    <aop:config>
<!--        pointcut id 自己定义切入点名称  execution代表在什么位置切入  -->
        <aop:pointcut id="myPointCut" expression="execution(* queryUser())" />
        <aop:pointcut id="myPointAfter" expression="execution(* saveUser(com.qf.domain.User ))" />
        <aop:pointcut id="myAdviceExceptionAfter" expression="execution(* updateUser(com.qf.domain.User ))" />
        <aop:pointcut id="myAdviceMethod" expression="execution(* deleteUser(Integer))" />
<!--     对queryUser方法进行切入  使用的是 myBeforeAdvice  -->
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPointCut" />
        <aop:advisor advice-ref="myAdviceAfter" pointcut-ref="myPointAfter" />
        <aop:advisor advice-ref="myAdviceException" pointcut-ref="myAdviceExceptionAfter" />
        <aop:advisor advice-ref="myAdviceMethodInterceptor" pointcut-ref="myAdviceMethod" />

    </aop:config>
</beans>

 5.统配切入点:根据表达式

<!--匹配参数-->
<aop:pointcut id="myPointCut" expression="execution(* *(com.qf.aaron.aop.basic.User))" />
<!--匹配方法名(无参)-->
<aop:pointcut id="myPointCut" expression="execution(* save())" />
<!--匹配方法名(任意参数)-->
<aop:pointcut id="myPointCut" expression="execution(* save(..))" />
<!--匹配返回值类型-->
<aop:pointcut id="myPointCut" expression="execution(com.qf.aaron.aop.basic.User *(..))" />
<!--匹配类名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.UserServiceImpl.*(..))" />
<!--匹配包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop.basic.*.*(..))" />
<!--匹配包名、以及子包名-->
<aop:pointcut id="myPointCut" expression="execution(* com.qf.aaron.aop..*.*(..))" />

6.JDK 和 CGlib 选择:

  • pring底层,包含了jdk代理和cglib代理两种动态代理生成机制

  • 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理

    class DefaultAopProxyFactory{
        // 该方法中明确定义了 JDK代理和CGLib代理的选取规则
        // 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
        public AopProxy createAopProxy(){...}
    }

    九、Mybatis Spring整合

 9.1 环境搭建:Pom.xml

    <dependencies>
        <dependency>
            <!--Spring依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!-- spring-jdbc 依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!-- spring+mybatis集成依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>
        
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            
        <!--德鲁伊连接池依赖-->
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <dependency>
            
        <!--测试依赖-->    
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

9.2 配置文件管理 

9.2.1 引入jdbc.properties 配置文件

#jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456

9.22 整合Spring配置文件和properties配置文件  Spring 管理 MyBatis 

9.221:配置Spring 多去properties

9.222:配置DruidDataSource连接池 在工厂中创建dataSource对象 得到dataSource

9.223:配置sqlSessionFactory 在工厂中生成sqlSessionFactory 得到连接

9.223: 配置MapperScannerConfigurer 管理Dao实现类的创建 并创建Dao对象存入到工厂

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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.xsd
       ">
    <!--配置文件参数化(参数占位符) 用Spring读取properties-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!--DruidDataSource连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--基本配置-->
        <property name="driverClassName" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!-- 工厂bean:生成SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入连接池 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- 注入dao-mapper文件信息 ,如果映射文件和dao接口 同包且同名,则此配置可省略-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/qf/dao/*.xml</value>
            </list>
        </property>
        <!-- 为 dao-mapper文件中的实体 定义缺省包路径
            如:<select id="queryAll" resultType="User"> 中 User类可以不定义包
        -->
        <!--<property name="typeAliasesPackage" value="com.qf.entity"></property>-->
        <!-- mapperScannerConfigurer -->
    </bean>
    <!-- mapperScannerConfigurer将所有的Dao接口自动生成Dao对象  存入到工厂中 -->
    <bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- dao接口所在的包  如果有多个包,可以用逗号或分号分隔
           <property name="basePackage" value="com.a.dao,com.b.dao"></property>
        -->
        <property name="basePackage" value="com.qf.spring.dao"></property>
        <!-- 如果工厂中只有一个SqlSessionFactory的bean,此配置可省略 -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>
</beans>

10、Spring事务

 10.1    配置DataSourceTransactionManager 事务管理器,其中持有DataSource,可以控制事务功能(commit,rollback等),DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource的Bean,否则事务控制失败

<!-- 1. 引入一个事务管理器,其中依赖DataSource,借以获得连接,进而控制事务逻辑 -->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

10.2   配置事务通知基于事务管理器,进一步定制,生成一个额外功能:Advice。此Advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务。(引入后tx xml不认识需要导入schema配置) ,

<tx:advice id="txManager" transaction-manager="tx">
    <!--定义事务属性-->
	<tx:attributes>
        <!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT"    
              	propagation="REQUIRED" read-only="false"/>-->
        <!-- 以User结尾的方法,切入此方法时,采用对应事务实行-->
        <tx:method name="*User" rollback-for="Exception"/>
        <!-- 以query开头的方法,切入此方法时,采用对应事务实行 -->
        <tx:method name="query*" propagation="SUPPORTS"/>
        <!-- 剩余所有方法 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--   schema 配置
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx
					http://www.springframework.org/schema/tx/spring-tx.xsd"
-->

10.3   事务属性 isolation="DEFAULT" 指的是事务隔离级别 隔离级别越高安全性越高 反之牺牲效率

名称        描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

10.3.1   特性:

安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。

并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。

10.3.2   并发问题:

问题描述
脏读一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止
不可重复读一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止。出现在update中
幻影读一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止。出现在insert、delete中

10.4   传播行为 propagation

当涉及到事务嵌套(Service调用Service)时,可以设置:SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询

REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改

10.5   读写性:read-only="false"(默认)

        true:只读,可提高查询效率。(适合查询)

        false:可读可写。 (默认值)(适合增删改

10.6   事务超时: timeout :(事务超时时间

当前事务所需操作的数据被其他事务占用,则等待

100:自定义等待时间100(秒)

-1:由数据库指定等待时间,默认值。(建议)

10.7   事务回滚:(自定义异常)

1.  如果事务中抛出 RuntimeException,则自动回滚

2. 如果事务中抛出 CheckException(非运行时异常 Exception),不会自动回滚,而是默认提交事务

3. 处理方案 : 将CheckException转换成RuntimException上抛,或 设置 rollback-for="Exception"

10.8   编织:将事务管理的Advice 切入需要事务的业务方法中(需要导入schema

<aop:config>
	<aop:pointcut expression="execution(* com.qf.spring.service.UserServiceImpl.*(..))" id="pc"/>
    <!-- 组织切面 -->
    <aop:advisor advice-ref="txManager" pointcut-ref="pc"/>
</aop:config>

11、注解开发

 11.1   声明Bean:

用于替换自建类型组建的<bean>标签 可以更快的声明bean

@Service        业务类专用
@Repositorydao层实现类专用(mapperScannerConfigurer)一般dao层不需要注解 在xml中已经配置自动生成dao对象
@ControllerWeb层专用
@Component通用(一般用于非三层架构其他类的包想注入到工厂中)
@Scope用户控制bean的创建模式 (singleton/prototype)

// @Service说明 此类是一个业务类,需要将此类纳入工厂  等价替换掉 <bean class="xxx.UserServiceImpl">
// @Service默认beanId == 首字母小写的类名"userServiceImpl"
// @Service("userService") 自定义beanId为"userService"
@Service //声明bean,且id="userServiceImpl"
@Scope("singleton") //声明创建模式,默认为单例模式 ;@Scope("prototype")即可设置为多例模式
public class UserServiceImpl implements UserService {
 	...   
}

11.2   注入(DI):用于完成bean中属性值的注入 基于注解式开发不需要set方法

@Autowired 

用于类型自动注入相当于(ByTapy)
@Resource基于名称自动注入
@Qualifier("userDAO")限定要自动注入的bean的id,一般和@Autowired联用
@Value注入简单类型数据 直接给属性赋值 (jdk8种+String)
@Service
public class UserServiceImpl implements UserService {
    
    @Autowired //注入类型为UserDAO的bean
    @Qualifier("userDAO2") //如果有多个类型为UserDAO的bean,可以用此注解从中挑选一个
    private UserDAO userDAO;
}
@Service
public class UserServiceImpl implements UserService {
    
	@Resource("userDAO3") //注入id=“userDAO3”的bean
    private UserDAO userDAO;
    /*
    @Resource //注入id=“userDAO”的bean
    private UserDAO userDAO;
    */
}
public class XX{
     @Value("100") //注入数字
    private Integer id;
    @Value("shine") //注入String
	private String name;
}

11.3  注解所需配置:

<!-- 告知spring,哪些包中 有被注解的类、方法、属性 -->
<!-- <context:component-scan base-package="com.qf.a,com.xx.b"></context:component-scan> -->
<context:component-scan base-package="com.qf"></context:component-scan>
	

11.4   事务控制:@Transactional标签 可以放在方法和类上 类上就是整个类执行事务

<!-- 告知spring,@Transactional在定制事务时,基于txManager=DataSourceTransactionManager -->
<tx:annotation-driven transaction-manager="txManager"/>

isolation = "隔离级别 默认default"

propagation = "传播行为 REQUIRED / SUPPORTS "

readOnly = "读写性:"false默认" 

rollbackFor = "回滚属性"

timeout = "事务超时时间默认-1"

//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED,readOnly=false,rollbackFor=Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {
	...
    //该方法自己的事务控制,仅对此方法有效
	@Transactional(propagation=Propagation.SUPPORTS)
	public List<User> queryAll() {
		return userDao.queryAll();
	}
	public void save(User user){
		userDao.save(user);
	}
}

11.5    AOP开发:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect // 声明此类是一个切面类:会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAspect {
    // 定义切入点
    @Pointcut("execution(* com.qf.spring.service.UserServiceImpl.*(..))")
    public void pc(){}
    
    @Before("pc()") // 前置通知
    public void mybefore(JoinPoint a) {
        System.out.println("target:"+a.getTarget());
        System.out.println("args:"+a.getArgs());
        System.out.println("method's name:"+a.getSignature().getName());
        System.out.println("before~~~~");
    }

    @AfterReturning(value="pc()",returning="ret") // 后置通知
    public void myAfterReturning(JoinPoint a,Object ret){
        System.out.println("after~~~~:"+ret);
    }
    
    @Around("pc()") // 环绕通知
    public Object myInterceptor(ProceedingJoinPoint p) throws Throwable {
        System.out.println("interceptor1~~~~");
        Object ret = p.proceed();
        System.out.println("interceptor2~~~~");
        return ret;
    }
    
    @AfterThrowing(value="pc()",throwing="ex") // 异常通知
    public void myThrows(JoinPoint jp,Exception ex){
        System.out.println("throws");
        System.out.println("===="+ex.getMessage());
    }
}
<!-- 添加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy


 

11.6   集成JUnit 依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

11.6.1   编码:可以免去工厂的创建过程  可以直接将要测试的组件注入到测试类

@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:applicationContext.xml") //spring的配置文件位置
public class SpringTest{//当前测试类也会被纳入工厂中,所以其中属性可以注入

    @Autowired // 注入要测试的组件
    @Qualifier("userDAO")
    private UserDAO userDAO;

    @Test
    public void test(){
        // 测试使用userDAO
        userDAO.queryUser();
        ....
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值