Spring注解和xml

1.注解/Annotation和xml

1.1DI相关

@Autowired可以单独使用,用于依赖注入的时候使用(DI)。
如果单独使用,则按类型连接(wired by type)。因此,如果容器中声明了多个相同类型的bean,就会出现问题,因为@Autowired不知道使用哪个bean来注入。因此,使用@Qualifier@Autowired来通过指定bean名字(通过名字来连接)来明确哪些bean需要被连接。

@Resource也是通过名称连接的,所以如果@AutowiredQualifier一起使用,它和@Resource是一样的。

区别在于@Autowired@Qualifier是spring注解,而@Resource是标准的java注解(来自JSR-250)。此外,@@Resource只支持字段和setter注入,而@Autowired支持字段、setter、构造函数和多参数方法注入。

建议对字段和setter注入使用@Resource,对于构造函数或者多参数方法注入,坚持使用@Qualifier@Autowired

1.2动态代理

JDK的动态代理
在这里插入图片描述

package cn.itcast.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        //1.创建真实对象
        Lenovo lenovo = new Lenovo();
        
        //2.动态代理增强lenovo对象
        /*
            三个参数:
                1. 类加载器:真实对象.getClass().getClassLoader()
                2. 接口数组:真实对象.getClass().getInterfaces()
                3. 处理器:new InvocationHandler()
         */
        SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {

            /*
                代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行
                    参数:
                        1. proxy:代理对象
                        2. method:代理对象调用的方法,被封装为的对象
                        3. args:代理对象调用的方法时,传递的实际参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*System.out.println("该方法执行了....");
                System.out.println(method.getName());
                System.out.println(args[0]);
*/
                //判断是否是sale方法
                if(method.getName().equals("sale")){
                    //1.增强参数
                    double money = (double) args[0];
                    money = money * 0.85;
                    System.out.println("专车接你....");
                    //使用真实对象调用该方法
                    String obj = (String) method.invoke(lenovo, money);
                    System.out.println("免费送货...");
                    //2.增强返回值
                    return obj+"_鼠标垫";
                }else{
                    Object obj = method.invoke(lenovo, args);
                    return obj;
                }
            }
        });

        //3.调用方法

       /* String computer = proxy_lenovo.sale(8000);
        System.out.println(computer);*/

        proxy_lenovo.show();
    }
}

cglib动态代理

在这里插入图片描述

1.3AOP(切面编程)

1.3.1XML配置

在这里插入图片描述

1.3.2注解开发

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.4Jdbc

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.Spring配置注解

2.1@Configuration的使用

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

注意@Configuration注解的配置类由如下要求:
1.@Configuration不可以是final类型;
2.@Configuration不可以是匿名类;
3.嵌套的configuration必须是 静态类;

2.1.1用@Configuration加载Spring

2.1.1.1@Configuration配置Spring并启动Spring容器

@Configuration标注在类上,相当于把该类作为Springxml配置文件中的<beans>,作用为:配置Spring容器(应用上下文)

package com.dxz.demo.configuration;

import org.springframework.context.annotation.Configuration;

@Configuration
public class TestConfiguration {
    public TestConfiguration() {
        System.out.println("TestConfiguration容器启动初始化。。。");
    }
}

相当于

<?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:jdbc="http://www.springframework.org/schema/jdbc"  
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false">


</beans>

主方法进行测试:

package com.dxz.demo.configuration;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMain {
    public static void main(String[] args) {

        // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);

        // 如果加载spring-context.xml文件:
        // ApplicationContext context = new
        // ClassPathXmlApplicationContext("spring-context.xml");
    }
}

从运行主方法结果可以看出,spring容器已经启动了:
在这里插入图片描述

2.1.1.2@Configuration启动容器+@Bean注册Bean

@Bean标注在方法上(返回某个实例的方法),等价于springxml配置文件中的<bean>,作用为:注册bean对象。

Bean类:

/**
 * @author chenzeyu
 */
public class TestBean {
    private String username;
    private String url;
    private String password;

    public void sayHello() {
        System.out.println("TestBean sayHello...");
    }

    @Override
    public String toString() {
        return "username:" + this.username + ",url:" + this.url + ",password:" + this.password;
    }

    public void start() {
        System.out.println("TestBean 初始化。。。");
    }

    public void cleanUp() {
        System.out.println("TestBean 销毁。。。");
    }
}

配置类:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author chenzeyu
 */
@Configuration
public class TestConfiguration {
    public TestConfiguration(){
        System.out.println("TestConfiguration容器初始化...");
    }
    
    //@Bean注解注册bean,同时可以指定初始化和销毁方法
    //@Bean(name="testBean",initMethod="start",destroyMethod="cleanUp")
    @Bean
    public TestBean  testBean() {
        System.out.println("testBean");
        // instantiate, configure and return bean ...
        return new TestBean();
    }
    @Bean
    public void Hello(){
        System.out.println("Hello");
    }

}

主方法测试类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author chenzeyu
 */
public class TestMain {
    public static void main(String[] args){
        //@Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
        ApplicationContext context =new AnnotationConfigApplicationContext(TestConfiguration.class);
        //如果加载Spring-context.xml文件:
        //ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
        //获取Bean
        TestBean tb = (TestBean) context.getBean("testBean");
        tb.sayHello();
    }
}

在这里插入图片描述
注:
(1)、@Bean注解在返回实例的方法上,如果未通过@Bean指定bean的名称,则默认与标注的方法名相同。
(2)、@Bean注解默认作用域为单例singleton作用域,可通过@Scope("prototype")设置为原型作用域;
(3)、既然@Bean的作用是注册bean对象,那么完全可以使用@Compontent@Controller@Service@Repository等注解注册Bean,当然需要配置@ComponentScan注解进行自动扫描。

2.1.1.3@Configuration启动容器+@Component注册Bean

Bean类:

package com.dxz.demo.configuration;

import org.springframework.stereotype.Component;

//添加注册bean的注解
@Component
public class TestBean {

    private String username;
    private String url;
    private String password;

    public void sayHello() {
        System.out.println("TestBean sayHello...");
    }

    public String toString() {
        return "username:" + this.username + ",url:" + this.url + ",password:" + this.password;
    }

    public void start() {
        System.out.println("TestBean 初始化。。。");
    }

    public void cleanUp() {
        System.out.println("TestBean 销毁。。。");
    }
}
2.1.1.4使用AnnotationConfigApplicationContext注册AppContext类的两种方法

(1)配置类的注册方式是将其传递给AnnotationConfigApplicationContext构造函数

public static void main(String[] args) {
        // @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
        //获取bean
        TestBean tb = (TestBean) context.getBean("testBean");
        tb.sayHello();
    }

(2)AnnotationConfigApplicationContext的register方法传入配置类来注册配置类

public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext();
  ctx.register(AppContext.class)
}
2.1.1.5配置Web应用程序(web.xml中配置AnnotationConfigApplicationContext)

过去,您通常利用XmlWebApplicationContext上下文来配置Spring Web应用程序,即在Web部署描述符文件Web.xml中指定外部XML上下文文件的路径。XMLWebApplicationContext是Web应用程序使用的默认上下文类。以下代码描述了web,xml中指向将由ContextLoaderListener监听器类载入的外部XML上下文文件的元素。

<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
    <servlet-name>sampleServlet</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    </servlet>
...
</web-app>

现在,您要将web.xml中的上述代码更改为使用ApplicationConfigApplicationContext类。切记XmlWebApplicationContext是Spring为Web应用程序使用的默认上下文实现,因此您永远不必在您的web.xml文件中显示指定这个上下文类。现在,您将使用基于Java的配置,因此在配置Web应用程序时,需要在web.xml文件中指定AnnotationConfigApplicationContext类。上述代码修改如下:

<web-app>
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.
            support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            demo.AppContext
        </param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
    <servlet-name>sampleServlet</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.
            support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    </servlet>

...
</web-app>

以上修改后的web.xml定义了AnnotationConfigWebApplicationContext上下文类,并将其作为上下文参数和servlet元素的一部分。上下文配置现在指向AppContext配置类。

2.1.1.6@Configuration总结

@Configuration等价于<Beans></Beans>
@Bean等价于<Bean></Bean>
@ComponentScan等价于<context:component-scan base-package="com.dxz.demo"/>

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
ConfigurationProperties

2.2@ConfigurationProperties的使用

官方文档建议将配置属性隔离到单独的pojo中。

@Configuration
@ConfigurationProperties(prefix="mail")
public class ConfigProperties{
    private String hostName;
    private int port;
    private String from;
    // standard getters and setters
}

我们使用@Configuration,这样Spring就可以在应用程序上下文中创建一个Spring Bean

@ConfigurationProperties最适合使用具有相同前缀的层次属性,因此,我们添加一个mail前缀。

Spring框架使用标准的Java bean setters,因此我们必须为每个属性声明setter

注意:如果我们在POJO中不使用@Configuration,那么我们需要在main Spring application class中添加@EnableConfigurationProperties(ConfigProperties.class),以将属性绑定到POJO中。

@SpringBootApplication
@EnableConfigurationProperties(ConfigProperties.class)
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

就是这样,Spring将自动绑定属性文件中定义的任何属性,这些属性具有前缀mail,并且与ConfigProperties类中的一个字段同名。

2.2.1Spring Boot 2.2

从Spring Boot2.2开始,Spring通过类路径扫描来查找和注册@ConfigurationProperties类。因此,不需要使用@Component(和其它元注释,如@Configuration)注释这样的类,甚至不需要使用@EnableConfigurationProperties

@ConfigurationProperties(prefix = "mail") 
public class ConfigProperties { 
 
    private String hostName; 
    private int port; 
    private String from; 
 
    // standard getters and setters 
}

@SpringBootApplication启用的类路径扫描器会找到ConfigProperties类,尽管我们没有使用@Component注释这个类。

除此之外,我们可以使用@ConfigurationPropertiesScan注释来扫描配置属性类的自定义位置。

@SpringBootApplication
@ConfigurationPropertiesScan("com.baeldung.properties")
public class DemoApplication { 
 
    public static void main(String[] args) {   
        SpringApplication.run(DemoApplication.class, args); 
    } 
}

2.3@PropertySource

@Value@ConfigurationProperties都可以用于获取配置文件的属性值,只是这两个注解在SpringBoot项目中都是获取默认配置文件的属性值,也就是application.ymlapplication.properties的属性值。

如果想引用其它配置文件的属性值,就可以使用@PropertySource注解。

新建一个user.properties的配置文件:

user.userName= root
user.isAdmin= true
user.regTime= 2019/11/01
user.isOnline= 1
user.maps.k1=v1
user.maps.k2=v2
user.lists=list1,list2
user.address.tel= 15899988899
user.address.name=上海

使用@PropertySource("classpath:user.properties")获取对应的properties文件,再用@ConfigurationProperties(prefix="user")进行属性映射:

package org.muses.jeeplatform.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
@PropertySource("classpath:user.properties")
@ConfigurationProperties(prefix = "user")
public class User {

    private String userName;
    private boolean isAdmin;
    private Date regTime;
    private Long isOnline;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Address address;

    @Override
    public String toString() {
        return "User{" +
                       "userName='" + userName + '\'' +
                       ", isAdmin=" + isAdmin +
                       ", regTime=" + regTime +
                       ", isOnline=" + isOnline +
                       ", maps=" + maps +
                       ", lists=" + lists +
                       ", address=" + address +
                       '}';
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public boolean isAdmin() {
        return isAdmin;
    }

    public void setAdmin(boolean admin) {
        isAdmin = admin;
    }

    public Date getRegTime() {
        return regTime;
    }

    public void setRegTime(Date regTime) {
        this.regTime = regTime;
    }

    public Long getIsOnline() {
        return isOnline;
    }

    public void setIsOnline(Long isOnline) {
        this.isOnline = isOnline;
    }

    public Map<String, Object> getMaps() {
        return maps;
    }

    public void setMaps(Map<String, Object> maps) {
        this.maps = maps;
    }

    public List<Object> getLists() {
        return lists;
    }

    public void setLists(List<Object> lists) {
        this.lists = lists;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

对应的Address类:

package org.muses.jeeplatform.bean;

/**
 * <pre>
 *
 * </pre>
 *
 * @author nicky
 * <pre>
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2019年11月03日  修改内容:
 * </pre>
 */
public class Address {
    private String tel;
    private String name;

    @Override
    public String toString() {
        return "Address{" +
                       "tel='" + tel + '\'' +
                       ", name='" + name + '\'' +
                       '}';
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

写一个Junit测试类:

package org.muses.jeeplatform;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.muses.jeeplatform.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@org.springframework.boot.test.context.SpringBootTest
public class SpringBootTest {

    @Autowired
    User user;

    @Test
    public void testConfigurationProperties(){
        System.out.println(user);
    }

}

读取成功,就不同将配置卸载默认的application.properties里了

User{userName='root', isAdmin=false, regTime=Fri Nov 01 00:00:00 CST 2019, isOnline=1, maps={k1=v1, k2=v2}, lists=[list1, list2], address=Address{tel='15899988899', name='上海市'}}

注意:对于@PropertySource注解,默认是不支持yml配置文件读取的,需要修改重写才可以。

2.4Mybatis相关注解

直接在Mapper类上面添加注解@Mapper,这种方式要求每一个mapper类都需要添加此注解,较麻烦。

通过使用@MapperScan可以指定要扫描的Mapper类的包的路径,比如:

@SpringBootApplication
@MapperScan("com.lz.water.monitor.mapper")
//添加对mapper包扫描
public class Application{
    
}
public static void main(String[] args){
    SpringApplication.run(Application.class, args);
}

2.4.1Spring Boot整合mybatis

基础的MyBatis用法中,是通过SqlSessionFactoryBuilder来创建SqlSessionFactory的。而在MyBatis-Spring中,则使用SqlSessionFactoryBean来创建。

设置
要创建工厂bean,将下面的代码放到Spring的XML配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

需要注意的是SqlSessionFactoryBean实现了SpringFactoryBean接口(参看通过工厂 bean 自定义实例化逻辑)。这意味着Spring最终创建的bean并不是SqlSessionFactoryBean本身,而是工厂类(SqlSessionFactoryBean)的getObject()方法返回的结果。这种情况下,Spring将会在应用启动时为你创建SqlSessionFactory,并使用sqlSessionFactory这个名字存储起来。

使用FactoryBean自定义实例化逻辑
您可以为本身就是工厂的对象实现org.springframework.beans.factory.FactoryBean接口
FactoryBean接口是Spring IoC容器实例化逻辑的可插入点
如果您有复杂的初始化代码(用Java来表达比(可能)冗长的XML更好),那么您可以创建自己的FactoryBean,在类中编写复杂的初始化,然后将定制的FactoryBean插入到容器中。
FactoryBean接口提供三种方法:

  • Object getObjects():返回此工厂创建的对象的实例,实例可能被共享,这取决于返回的是单例还是原型。
  • boolean isSingleton():如果FactoryBean返回单例,返回true;否则返回false
  • Class getObjectType():返回getObject()方法返回的对象类型,如果事先不知道该类型,则返回null

FactoryBean的概念和接口在Spring框架的许多地方使用。Spring本身附带了50多个FactoryBean接口的实现。
当使用ApplicationContext的getBean()方法获取FactoryBean实例本身而不是它所产生的bean,则要使用&符号+id。比如,现有FactoryBean,它有id,在容器上调用getBean(“myBean”)将返回FactoryBean所产生的bean,调用getBean("&myBean")将返回FactoryBean它本身的实例。

通过工厂bean自定义实例化逻辑

等效的Java代码如下:

@Bean
public SqlSessionFactory sqlSessionFactory() {
  SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
  factoryBean.setDataSource(dataSource());
  return factoryBean.getObject();
}

通常,在MyBatis-Spring中,你不需要直接使用SqlSessionFactoryBean或者对应的SqlSessionFactory。相反,session的工厂bean将会被注入到MapperFactoryBean或其它继承于SqlSessionDaoSupport的DAO(Data Access Object,数据访问对象)中。

/*
*整合mybatis SqlSessionFactoryBean
*/
@Bean
public SqlSessionFactoryBean mysqlCoreSqlSessionFactory(@Qualifier("mysqlCoreDataSource") DataSource mysqlCoreDataSource) throws IOException {

        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        //数据源
        sessionFactory.setDataSource(mysqlCoreDataSource);
        
        //别名     
        sessionFactory.setTypeAliasesPackage(this.getAliasesPackage());
        //mapper文件存储的位置
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sessionFactory.setMapperLocations(resolver.getResources(this.getMapperFilePath()));
        //开启驼峰标识 user_name --> userName
        org.apache.ibatis.session.Configuration mybatisConf = new org.apache.ibatis.session.Configuration();
        mybatisConf.setMapUnderscoreToCamelCase(true);
        sessionFactory.setConfiguration(mybatisConf);
        return sessionFactory;
    }

属性
sqlSessionFactory有一个唯一的必要属性:用于JDBC的DataSource。这可以是任意的DataSource对象,它的配置方法和其它Spring数据库连接是一样的。
一个常用的属性是configLocation,它用来指定MyBatis的XML配置文件路径。它在需要修改MyBatis的基础配置非常有用。通常,基础配置指的是<settings>或者<typeAliases>元素。
需要注意的是,这个配置文件并不需要是一个完整的MyBatis配置,确切地说,任何环境配置(<environments>),数据源(<DataSource>)和MyBatis的事务管理器(<transactionManager>)都会被忽略SqlSessionFactoryBean会创建它自有的MyBatis环境配置(Environment),并按要求设置自定义环境的值。

如果MyBatis在映射器类对于的路径下找不到与之对应的映射器XML文件,那么也需要配置文件。这时有两种解决办法:第一种是手动在MyBatisXML配置文件中的<mappers>部分指定XML文件的类路径;第二种是设置工厂bean 的mapperLocations属性。
mapperLocations属性接受多个资源位置,这个属性可以用来指定MyBatis的映射器XML配置文件的位置。属性的值是一个Ant风格的字符串,可以指定加载一个目录中的所有文件,或者从一个目录开始递归搜索所有目录。比如

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml"/>
</bean>

这会从类路径下加载所有在sample.config.mappers包和它的子包中的MyBatis映射器XML配置文件。

在容器管理事务的时候,您可能需要的一个属性是transactionFactoryClass
自1.3.0版本开始,新增的configuration属性能够在没有对应的MyBatis XML配置文件的情况下,直接设置Configuration实例。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configuration">
    <bean class="org.apache.ibatis.session.Configuration">
      <property name="mapUnderscoreToCamelCase" value="true"/>
    </bean>
  </property>
</bean>

2.4.2事务管理

2.4.2.1@EnbaleTransactionManagement

启用Spring的注解进行事务管理的功能,类似于Spring的支持<tx:*> XML命名空间。用于@Configuration类,以配置传统的、命令式事务管理或者反应性事务管理。
下面的示例演示使用PlatformTransactionManager进行命令式事务管理。对于反应性事务管理,配置一个Reactive TransactionManager替代。

@Configuration
 @EnableTransactionManagement
 public class AppConfig {

     @Bean
     public FooRepository fooRepository() {
         // configure and return a class having @Transactional methods
         return new JdbcFooRepository(dataSource());
     }

     @Bean
     public DataSource dataSource() {
         // configure and return the necessary JDBC DataSource
     }

     @Bean
     public PlatformTransactionManager txManager() {
         return new DataSourceTransactionManager(dataSource());
     }
 }

为了便于参考,可以将上面的示例与下面的Spring XML配置进行比较

 <beans>

     <tx:annotation-driven/>

     <bean id="fooRepository" class="com.foo.JdbcFooRepository">
         <constructor-arg ref="dataSource"/>
     </bean>

     <bean id="dataSource" class="com.vendor.VendorDataSource"/>

     <bean id="transactionManager" class="org.sfwk...DataSourceTransactionManager">
         <constructor-arg ref="dataSource"/>
     </bean>

 </beans>

在上述两个场景中,@EnableTransactionManagement<tx:annotation-driven/>负责注册支持注释驱动的事务管理的必要Spring组件。例如,TransactionInterceptor和基于代理或者AspectJ的通知,当jdbcFooRepository's@Transactional方法被调用时,这些通知将拦截器编织到调用堆栈中。

两个示例之间的一个小区别在于TransactionManagerbean的命名。在@Bean情况下,名称是txManager(根据方法的名称)。在XML中,名称是transactionManager

2.5Spring加载资源文件

2.5.1资源查找器PathMatchingResourcePatternResolver

PathMatchingResourcePatternResolverResourcePatternResolver的常用实现类,下面从源码中看看spring是如何加载多个资源文件的。

构造器

	/**
	 * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
	 * <p>ClassLoader access will happen via the thread context class loader.
	 * @see org.springframework.core.io.DefaultResourceLoader
	 * 无参构造器,构造了一个DefaultResourceLoader实例作为ResourceLoader
	 */
	public PathMatchingResourcePatternResolver() {
		this.resourceLoader = new DefaultResourceLoader();
	}

	/**
	 * Create a new PathMatchingResourcePatternResolver.
	 * <p>ClassLoader access will happen via the thread context class loader.
	 * @param resourceLoader the ResourceLoader to load root directories and
	 * actual resources with
	 * 传进来一个自定义的ResourceLoader
	 */
	public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		this.resourceLoader = resourceLoader;
	}

	/**
	 * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
	 * @param classLoader the ClassLoader to load classpath resources with,
	 * or {@code null} for using the thread context class loader
	 * at the time of actual resource access
	 * @see org.springframework.core.io.DefaultResourceLoader
	 * 构造了一个指定了ClassLoader的DefaultResourceLoader
	 */
	public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {
		this.resourceLoader = new DefaultResourceLoader(classLoader);
	}

getResource方法

	/**
	 * Return the ResourceLoader that this pattern resolver works with.
	 */
	public ResourceLoader getResourceLoader() {
		return this.resourceLoader;
	}
	@Override
	public Resource getResource(String location) {
	    //委托给了getResourceLoader()返回值获取Resource
	    //getResourceLoader()的返回值就是构造器产生的ResourceLoader
	    //所以呢,如果在实例化PathMatchingResourcePatternResolver是不知道ResourceLoader,那么使用的就是DefaultResourceLoader
		return getResourceLoader().getResource(location);
	}

getResources方法

	@Override
	public Resource[] getResources(String locationPattern) throws IOException {
		Assert.notNull(locationPattern, "Location pattern must not be null");
		//以classpath*:开头
		if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
			// a class path resource (multiple resources for same name possible)
			//路径是否包含通配符 * 或者 ?ant风格
			if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
				// a class path resource pattern
				return findPathMatchingResources(locationPattern);
			}
			else {
				// all class path resources with the given name
				// 路径不包含通配符 * 或 ?
				// 返回classpath下的所有Resource
				return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
			}
		}
		else {
			// Generally only look for a pattern after a prefix here,
			// and on Tomcat only after the "*/" separator for its "war:" protocol.
			// 计算路径前缀的长度,从后面截串,war:是针对tomcat解析用的 */是ant风格的路径模式
			int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
					locationPattern.indexOf(':') + 1);
			// 路径是否包含通配符 * 或者 ?
			if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
				// a file pattern
				// 返回匹配的路径的Resource
				return findPathMatchingResources(locationPattern);
			}
			else {
				// a single resource with the given name
				// 返回单个Resource
				return new Resource[] {getResourceLoader().getResource(locationPattern)};
			}
		}
	}

getResources方法的逻辑并不复杂,把资源查找分成了3类:
1、解析后路径中存在ant风格的资源路径,使用findPathMatchingResources查找资源
2、以classpath*:开头,且不存在ant风格的资源路径,使用findAllClassPathResource查找资源
3、其它路径使用getResource查找单个资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值