注解编程

注解编程:指的是在类或者方法上加入特定的注解(@XXX)来完成特定功能的开发。

为什么要注解编程?

  1. 注解开发方便,代码简洁,开发速度大大提高
  2. Spring的开发潮流(从2.x版本引入注解,3.x版本完善,到SpringBoot普及)

环境准备

首先要在maven的pom包中引入Spring的相关jar包(这个不用说),然后再applicationContext.xml中加入

<context:component-scan base-package="com.prince" />

这个标签的作用是开启注解编程,并告诉Spring需要搜索的包。

基础注解

对象创建相关注解

@Component

作用:替换掉原有Spring配置文件中的bean标签
用法:直接在类前面加上该注解

@Component
public class User {

其中

  • id属性,默认值是类名首字母小写(如User -> user,UserDao -> userDao),可以通过value属性来指定
  • class属性:通过反射获取

测试:

@Test
public void test(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicaationContext.xml");
    User user = applicationContext.getBean("user", User.class);
    System.out.println("user = " + user);
}

衍生注解:(效果完全一样,只是名字不同而已,功能没有区别)

@Repository
@Service
@Controller
@Scope

作用:控制简单对象的创建次数(等同于原来bean标签的scope属性)

<bean id="" class="" scope="singleton|prototype"></bean>

用法:直接在类前面加上注解

@Component
@Scope("singleton")
public class User {

注意:默认值singleton

@Lazy

作用:延迟创建单实例对象(替换掉原来bean标签的lazy属性)

<bean id="" class="" lazy="false"></bean>

用法:在类前面加上该注解,代表延迟加载,只在获取该对象的时候才会创建。

@Component
@Lazy
public class User {

与生命周期相关的注解

使用到两个注解:

  1. 初始化相关方法:@PostConstruct
  2. 销毁方法:@PreDestroy

用法:直接在类的对应方法前面加上该注解即可

(注意导入javax的包,这两个注解是java提供的而不是spring)

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


@Component
public class Product {

    @PostConstruct
    public void myInit(){
        System.out.println("Product.myInit");
    }

    @PreDestroy
    public void myDestroy(){
        System.out.println("Product.myDestroy");
    }
}

测试:

@Test
public void test2(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicaationContext.xml");//工厂创建,对象创建,调用初始化方法
    applicationContext.close(); //工厂关闭,对象销毁,调用销毁方法
}

注意:这两个注解不是Spring提供的,而是JSR(JAVAEE规范)520,这再一次地验证,通过注解实现接口的契约型。

注入相关注解

@AutoWired

作用:把这个注解加在成员变量或者setter方法上,Spring就会自动根据 类型 进行注入。这个注解主要用于 用户自定义类型 的注入

比如在UserService里注入UserDao:

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("UserDaoImpl.insert");
    }
}
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

细节:

  • 如果想根据id注入,而不是类型,可以和@Qualifier注解配合使用
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
  • JavaEE规范JSR250中有类似功能的注解@Resource(name = ""),基于id值进行注入,等同于@Autowired@Qualifier的结合
//import javax.annotation.Resource;
@Resource(name = "userDaoImpl")
private UserDao userDao;

当不指定name属性的时候,将会按照类型进行注入

  • JavaEE规范的JSR330中还有一个注解@Inject,作用和@Autowired完全一致,使用时需要导入依赖:
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
@Value

这个注解主要用于JDK内置类型的注入,使用方法:直接在成员变量前面加上注解。

@Component
public class User {
    @Value("123456")
    private Integer id;
    @Value("abcabc")
    private String username;

为了解耦合,可以把Value里的值都抽取到一个properties文件里,然后让Spring读取这个配置文件来进行注入(配置文件参数化)。

id = 123456
username = abcabc

在Spring 的applicationContext.xml中配置,读取properties文件

<context:property-placeholder location="classpath:/user.properties" />

然后@Value注解中使用EL表达式:

@Component
public class User {
    @Value("${id}")
    private Integer id;
    @Value("${username}")
    private String username;

注意:

  • @Value注解不能放在静态成员变量前面,如果放,也是注入失败。
  • @Value + Properties的注入方式不支持集合类型,如果需要,需要用到Spring提供的新配置形式 YAMLYML
  • 可以使用@PropertySource注解来代替xml中的配置<context:property-placeholder location="classpath:/user.properties" />
@Component
@PropertySource("classpath:/user.properties")
public class User {
    @Value("${id}")
    private Integer id;
    @Value("${username}")
    private String username;

注解扫描

开启注解扫描:自动扫描指定包及其子包下的所有类。

<context:component-scan base-package="com.prince" />

排除方式

context:component-scan标签内部嵌套context:exclude-filter标签即可!

<context:component-scan base-package="com.prince" >
        <context:exclude-filter type="" expression=""/>
</context:component-scan>

javaioc注解案例 java 注解编程_bc

type属性共有5中取值:

  1. aspectj
    通过切入点表达式(类切入点 和 包切入点)进行排除。
<!-- 排除com.prince.service包及其子包下的所有类 -->
<context:exclude-filter type="aspectj" expression="com.prince.service..*"/>
<!-- 排除任意包下所有名为User的类 -->
<context:exclude-filter type="aspectj" expression="*..User"/>

注意:使用需要导入aspect的相关依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
  1. annotation
    排除使用指定注解的类
<!-- 排除使用@Service注解的类 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
  1. assignable
    排除指定类型
<!-- 排除User类 -->
<context:exclude-filter type="assignable" expression="com.prince.bean.User"/>
  1. regex:正则 custom:自定义
    这两个使用得比较少

包含方式

和排除方式一样,这个嵌套的是context:include-filter

<context:component-scan base-package="com.prince" use-default-filters="false">
    <context:include-filter type="" expression=""/>
</context:component-scan>

其中type和expression和排除方式是一模一样的。

主要是包含方式要指定use-default-filters="false",表示让Spring默认的注解扫描失效,只有指定了这个注解的值为false,Spring才会只扫描context:include-filter中配置的类,否则Spring还是会对base-package指定的包及其子包全部扫描进去。

高级注解

Spring在3.x中提供的注解,用于替换XML配置文件。

配置类

不再使用ApplicationContext.xml的配置文件,取而代之的是由@Configuration标注的配置类。

@Configuration
public class AppConfig {
}

javaioc注解案例 java 注解编程_spring_02

创建ApplicationContext类的时候,不再使用ClassPathApplicationContext,而是使用AnnotationConfigApplicationContext

//使用AppConfig这个配置类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
//扫描com.prince.config包及其子包下所有的配置类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.prince.config");

@Configuration注解的本质:也是@Component的衍生注解:

javaioc注解案例 java 注解编程_User_03

日志

不推荐log4j,使用logback作为日志框架:

  1. 先引入logback相关依赖
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.30</version>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>

<dependency>
    <groupId>org.logback-extensions</groupId>
    <artifactId>logback-ext-spring</artifactId>
    <version>0.1.5</version>
</dependency>
  1. 引入logback.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 控制台输出 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出: %d表示⽇期, %thread表示线程名,
            %-5level:级别从左显示5个字符宽度%msg:⽇志消息, %n是换⾏符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

@Bean注解的使用

@Bean注解在配置bean中使用,等同于XML配置中的bean标签

@Bean注解加到一个方法中,返回值是要创建的对象的类型,方法名就是id值,再把对象创建的代码放到方法体中即可!

javaioc注解案例 java 注解编程_java_04

  • 创建简单对象
@Bean
public User user(){
    User user = new User();
    user.setId(10086);
    user.setUsername("prince");
    return user;
}
  • 创建复杂对象
@Bean
public Connection connection(){
    try {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///mydata","root","root");
        return conn;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
  • 使用FactoryBean创建复杂对象(不常用)
import org.springframework.beans.factory.FactoryBean;

import java.sql.Connection;
import java.sql.DriverManager;

public class ConnectFactoryBean implements FactoryBean<Connection> {
    @Override
    public Connection getObject() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql:///mydata","root","root");
        return conn;
    }

    @Override
    public Class<?> getObjectType() {
        return Connection.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

在配置类中直接引用这个ConnectFactoryBean的getObject即可

@Bean
public Connection connection1(){
    Connection connection = null;
    try {
        connection = new ConnectFactoryBean().getObject();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return connection;
}

细节:

  • 自定义id值,默认是取方法名作为默认的id值,也可以给Bean注解加上参数作为id值
@Bean("u")		//创建一个对象,id值是u
public User user(){
  • 控制对象创建次数
@Bean("u")
@Scope("prototype")
public User user(){

@Bean注解的注入

用户自定义类型

原来XML中的配置是这样的:

<bean id="userDao" class="com.prince.dao.UserDaoImpl" />
<bean id="userService" class="com.prince.service.UserServiceImpl">
    <property name="userDao" ref="userDao" />
</bean>

换成注解之后是这样的:

  • UserDao对象的创建:
@Bean
public UserDao userDao(){
    return new UserDaoImpl();
}
  • UserService的创建,由于需要依赖注入上面的UserDao,所以要先获取到我们创建的UserDao再通过setter注入,有两种方法:
  1. 直接把UserDao当成函数的参数传进去
@Bean
public UserService userService(UserDao userDao){
    UserServiceImpl userService = new UserServiceImpl();
    userService.setUserDao(userDao);
    return userService;
}
  1. 第二种方法更为简单,不需要传参数,直接在调用setUserDao的时候把函数传进去
@Bean
public UserService userService(){
    UserServiceImpl userService = new UserServiceImpl();
    userService.setUserDao(userDao());
    return userService;
}
JDK类型
@Bean
public User user(){
    User user = new User();
    user.setId(10086);
    user.setUsername("prince");
    return user;
}

细节:可以配合@PropertySource@Value注解,把要注入的值放到配置文件中,进行解耦合

@Configuration
@PropertySource("classpath:/user.properties")
public class AppConfig {
    @Value("${id}")
    private Integer id;
    @Value("${name}")
    private String username;

    @Bean
    public User user(){
        User user = new User();
        user.setId(id);
        user.setUsername(username);
        return user;
    }

@ComponentScan

注解扫描注解

@Configuration
@ComponentScan(basePackages = "com.prince")
public class AppConfig1 {

排除策略:(和XML的类似)

@Configuration
@ComponentScan(basePackages = "com.prince",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class}),
                @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.prince.dao..*"})
        }
)
public class AppConfig2 {
}

包含策略:(和XML的一样,也要先useDefaultFilters = false,取消默认的扫描策略)

@Configuration
@ComponentScan(basePackages = "com.prince",
        useDefaultFilters = false,
        includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {UserService.class}),
                @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = {"com.prince.dao..*"})
        }
)
public class AppConfig2 {
}

多配置创建对象的优先级

配置Bean创建对象的优先级:@Component注解创建的对象 < @Bean注解创建的对象 < XML中bean标签创建的对象

高优先级可以覆盖低优先级(覆盖的前提是对象的ID值保持一致!

怎么同时使用配置类和配置文件?加入@ImportResource注解。

@Configuration
@ImportResource("classpath:/applicationContext.xml")
public class AppConfig2 {
}

为了解决耦合问题,比如前面的UserDao

@Bean
public UserDao userDao(){
    return new UserDaoImpl();
}

当以后如果需要更改UserDao的实现类,不再是UserDaoImpl,需要改成新的UserDaoImplNew的时候,就不需要修改源代码,直接用配置覆盖的方式,重新在XML中配置即可:

<bean id="userDao" class="com.prince.dao.UserDaoImplNew" />

这样子获取id为userDao的对象的时候,获取的是UserDaoImplNew而不是UserDaoImpl

多配置信息的整合

  • 为什么会有多个配置信息?
    拆分多个配置类的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想。
  • 多配置信息整合的方式
  1. 多个配置类的整合
  2. 配置类和@Component相关注解的整合
  3. 配置类和XML配置文件的整合
  • 整合多种配置需要关注的要点
  1. 如何使多配置的信息汇总成一个整体
  2. 如何实现跨配置的注入(一个配置类创建的对象中注入另一个配置类创建的对象)
多个配置类的整合
  1. 在创建ApplicationContext的时候扫描整个包下的所有配置类
//扫描com.prince.config包及其子包下所有的配置类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.prince.config");
  1. 再其中一个配置类中用@Import注解来引入另一个配置类
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {

然后再创建ApplicationContext的时候,只需要传入AppConfig1.class就可以同时读取两个配置类的信息

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig1.class);

这样子等同于XML中的配置:

<import resource="classpath:applicationContext1.xml" />
  1. 在AnnotationConfigApplicationContext中指定多个配置类(可变参数)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class);

跨配置类的对象注入:

@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {
    @Bean
    public UserDao userDao(){
        return new UserDaoImpl();
    }
}
//===================================================
//AppConfig2中创建的UserService,需要注入AppConfig1中创建的UserDao,
//只需要在AppConfig2中创建一个成员变量,并加上@Autowired注解即可。
//===================================================
@Configuration
public class AppConfig2 {

    @Autowired
    private UserDao userDao;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
}

**在应⽤配置类的过程中,不管使⽤哪种⽅式进⾏配置信息的汇总,其操作⽅式都是通过成员变量加⼊@Autowired注解完成。 **

配置类和@Component相关注解的整合

使用@ComponentScan注解来扫描所有@Component创建的对象,注入的时候也是采用@Autowired的方式

@Configuration
@ComponentScan("com.prince.dao")
public class AppConfig2 {

    @Autowired
    private UserDao userDao;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
}
@Component
public class UserDaoImpl implements UserDao{
配置类与配置文件的的整合

使用@ImportResource来引入XML配置文件

@Configuration
@ImportResource("classpath:applicationContext.xml")
public class AppConfig2 {

    @Autowired
    private UserDao userDao;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        return userService;
    }
}
<bean id="userDao" class="com.prince.dao.UserDaoImpl" />

纯注解版AOP编程

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl {
    public void register(){
        System.out.println("UserServiceImpl.register");
    }

}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect //切面类
@Component
public class MyAround {

    @Around("execution(* com.prince.aop..*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("====================log=====================");
        Object ret = joinPoint.proceed();
        return ret;
    }
}
@Configuration
@EnableAspectJAutoProxy //等同于<aop:aspectj-autoproxy />
@ComponentScan("com.prince.aop")
public class AopConfig {
}

细节分析:

  1. 代理创建模式的切换(JDK / Cglib)
    原XML中是这样子设置的
<aop:config proxy-target-class="true">
</aop:config>
<!--或者-->
<aop:aspectj-autoproxy proxy-target-class="true" />

注解的方式:

@EnableAspectJAutoProxy(proxyTargetClass = true)
  1. SpringBoot中AOP的开发方式,只需要前两步(原始对象、切面类),不需要@EnableAspectJAutoProxy,已经默认设置好了。而且SpringAOP的默认代理实现是JDK,而SpringBoot AOP的默认实现是CGlib。

纯注解版Spring+Mybatis整合

整合
  1. druid连接池
    XML 中的配置:
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
 <property name="url" value="jdbc:mysql://localhost:3306/mydata"/>
    <property name="username" value="root" />
 <property name="password" value="root" />
</bean>

配置类中的配置:

@Bean
public DataSource dataSource(){
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/mydata");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    return dataSource;
}
  1. SqlSessionFactoryBean
    XML 中的配置:
<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="factoryBean">
 <!--指定DataSource数据库连接池-->
    <property name="dataSource" ref="dataSource" />
 <!--给实体类设置别名-->
    <property name="typeAliasesPackage" value="com.prince.entity" />
    <!--设置mapper.xml的文件路径-->
    <property name="mapperLocations">
     <list>
            <value>mapper/*Mapper.xml</value>
        </list>
    </property>
</bean>

配置类中的配置:(注意需要注入上一步创建的DataSource对象)

@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);  //注入DataSource
    sqlSessionFactoryBean.setTypeAliasesPackage("com.prince.mybatis");
    sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("mapper/UserDaoMapper.xml"));
    return sqlSessionFactoryBean;
}
  1. MapperScannerConfigure
    XML 中的配置:
<!--配置MapperScannerConfigurer-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer">
<!--SqlSessionFactoryBean-->
   <property name="sqlSessionFactoryBeanName" value="factoryBean" />
<!--指定Dao接口所在的包-->
   <property name="basePackage" value="com.prince.dao" />

</bean>

配置类中的配置:
这个我们不需要专门去创建一个MapperScannerConfigurer,Spring已经为我们提供了一个注解。直接把这个注解加到配置类上面就行了。

@Configuration
@ComponentScan("com.prince.mybatis")
@MapperScan(basePackages = {"com.prince.mybatis.dao"})
public class MybatisConfig {

@MapperScan注解只需要指定一个basePackages,表示Dao接口存放的位置。不需要专门指定sqlSessionFactoryBeanName,因为把注解加到类上,Spring还是可以扫描到这个类里创建的SqlSessionFactoryBean,而且创建SqlSessionFactoryBean的时候id值是可以任意取的。

完整代码:

@Configuration
@ComponentScan("com.prince.mybatis")
@MapperScan(basePackages = {"com.prince.mybatis.dao"})
public class MybatisConfig {

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mydata");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);  //注入DataSource
        sqlSessionFactoryBean.setTypeAliasesPackage("com.prince.mybatis");
        sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("mapper/UserDaoMapper.xml"));
        return sqlSessionFactoryBean;
    }

}
细节
  • MapperLocations编码时通配的写法
    在XML配置中,MapperLocations是可以直接写通配符的:
<property name="mapperLocations">
    <list>
        <value>mapper/*Mapper.xml</value>
    </list>
</property>

而注解配置中,这样子配置是会报错的

sqlSessionFactoryBean.setMapperLocations(new ClassPathResource("mapper/*Mapper.xml"));

解决办法:使用 ResourcePatternResolver ( PathMatchingResourcePatternResolver )

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("mapper/*Mapper.xml");
sqlSessionFactoryBean.setMapperLocations(resources);

因为sqlSessionFactoryBean.setMapperLocations的参数是Resource类型的可变参数,所以可以直接传入一个数组。

  • 配置类中数据的耦合问题
    解决:数据库的DriverClassName,URL,username,password可以抽取到一个properties配置文件中,然后再利用@PropertySource@Value注解。
事务

原来的XML配置版本:

<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="dataSourceTransactionManager">
    <!--传入dataSource数据库连接池-->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--组装切面-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" />

<bean class="com.prince.service.UserServiceImpl" id="userService">
    <!--Spring与Mybatis整合的时候会创建一个UserDao,直接把这个UserDao注入即可-->
    <property name="userDao" ref="userDao"/>
</bean>

注解配置版本:

  • TransactionManager
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(){
    DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
    dataSourceTransactionManager.setDataSource(dataSource());
    return dataSourceTransactionManager;
}
  • tx:annotation-driven:
@EnableTransactionManagement(proxyTargetClass = true)
public class MybatisConfig {

(注解一样不需要手动输入TransactionManager的id值,因为注解加到类上,Spring可以扫描这个类的所有TransactionManager)
有一个bug,使用JDK动态代理的时候会报错,我也不知道是什么原因,用cglib没问题,所有我就加上了proxyTargetClass = true

  • UserService:
@Service
@Transactional
public class UserServiceImpl implements UserService{
    @Autowired
    UserDao userDao;

    @Override
    public void insert(User user) {
        userDao.insert(user);
    }

    @Override
    public User login(String username, String password) {
        return userDao.login(username,password);
    }
}

(使用了@Service@Autowired还完成对象创建)

YAML

YML(YAML)是⼀种新形式的配置⽂件,比XML更简单,比Properties更强大。

Properties配置问题:

  1. 表达过于繁琐,无法表达数据的内在联系。
  2. 无法表达对象、集合类型

语法

  1. 基本语法(key: value的格式,注意冒号后面需要留一个空格!
name: prince
password: 123456
  1. 对象(注意缩进!
person: 
   name: prince
   password: 123456
  1. 集合(注意缩进!!每一个值以-开头,并留一个空格
list:
    - 111111
    - 222222