一、注解实例化
1.Java注解开发介绍
-
Spring支持哪些编程风格开发?
-
xml编程
<beans> <bean> <property /> <constructor-arg /> </bean> </beans>
-
注解编程
@Component @Bean ...
-
-
哪种编程风格好?
-
注解编程优点:简化了书写,降低了 配置维护,提高了开发效率
-
注解编程缺点:不像xml那么结构明了
-
总结:
-
在企业追逐开发效率过程中注解已经成为一种主流趋势!
-
xml能够实现的,注解同样可以实现!
-
-
2.@Component实例化普通bean
-
场景1:使用 就可以实现对普通类进行实例化,那对应的注解怎么实现呢?
-
注解:@Component注解可以等价,作用于类,Spring会扫描组件类然后实例化
//给实例化的bean的取一个名字"userService" @Component(“userService”) public class UserServiceImpl implements UserService{} //不给实例化的bean取名字,默认按照类的首字母小写来声明,即bean的名称为“userServiceImpl” @Component public class UserServiceImpl implements UserService{}
- @Controller(用于区分表现层) @Service(用于区分业务层) @Repository(用于区分数据层) 继承了@Component
- @Configuration也继承了@Component 且代表Spring配置,相当于applicationContext.xml
-
开关:一个注解是无法起作用的,需要打开开关@ComponentScan
@Configuration @ComponentScan(basepackage={"com.itheima"}) public class SpringConfig { }
-
作用1:扫描组件实例化bean
从提供的包及其子包中扫描@Component @Controller @Service @Repository @Configuration 等组件注解,扫描完之后,转化成bean交给spring管理
-
作用2:激活@Autowired @Resource 依赖注入注解 @PostConstruct 初始化注解 @PreDestroy 销毁注解 等组件内注解
-
-
-
普通bean实例化演示:
-
@Component代码演示
-
在UserServiceImpl上加@Component注解
@Service("userService") public class UserServiceImpl implements UserService {}
-
创建Spring的配置类@Configuration,并且扫描UserServiceImpl组件
@Configuration @ComponentScan(basePackages = "com.itheima.service.impl") public class SpringConfig { }
-
测试: 通过AnnotationConfigApplicationContext来加载SpringConfig配置类
@Test public void testBeanInstanceByAnnotation() { ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); userService.save(); }
-
-
@Controller@Service @Repository代码演示
-
创建controller、service、repository层,分别加上@Controller @Service @Repository组件注解
@Repository("userDao") public class UserDaoImpl implements UserDao {}
@Service("userService") public class UserServiceImpl implements UserService {}
@Controller public class UserController {}
-
在Spring的配置类扫描三层中所有组件
@Configuration //扫描com.itheima包及其子包都会被扫描到 @ComponentScan(basePackages = "com.itheima") public class SpringConfig { }
-
测试:打印出Spring容器中的所有bean的名称
@Test public void testBeanAnnotationByAnnotation() { ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); String[] names = context.getBeanDefinitionNames(); for (String name : names) { System.out.println("name = " + name); } }
-
总结:
1.@Component、@Controller、@Service、@Repository注解+@ComponentScan注解开关使用 2.@Configuration配置类
-
-
-
场景2:在<bean class="" scope="" init-method=""…> 注解如何实现呢?
- 注解:@Scope @PostConstruct @PreDestroy @Lazy
- 开关:@ComponentScan(basePackages = “”)
-
属性注解演示:
-
@Scope、@PostConstruct @PreDestroy、@Lazy
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { @PostConstruct public void myInit() { System.out.println("UserServiceImpl.myInit自定义初始化方法"); } @PreDestroy public void myDestroy() { System.out.println("UserServiceImpl.myDestroy自定义销毁方法"); } }
-
测试
@Test public void testBeanAttributeByAnnotation() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 1.@Scope //UserService userService1 = context.getBean("userService", UserService.class); //UserService userService2 = context.getBean("userService", UserService.class); //Assert.assertSame(userService1, userService2); // 2.@PostConstruct @PreDestroy //context.close(); // 3.@Lazy }
-
3.@Bean实例化工厂bean
-
场景:SqlSessionFactoryBean该类不能加@Component实例化,那这种情况下该如何使用注解实例化呢?
-
注解:@Bean 作用于一个方法产生一个由 Spring 容器管理的 bean。
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Bean { @AliasFor("name") String[] value() default {}; }
-
@Bean作用代码演示
@Configuration public class UserServiceFactory { //1.静态工厂方法,可以给bean取多个名称 @Bean({"u1", "userService1"}) public static UserService createUserService() { return new UserServiceImpl(); } //2.非静态工厂方法,默认为bean的名称为方法名称 @Bean public UserService createUserService2() { return new UserServiceImpl(); } }
-
案例:使用@Bean对SqlSessionFactoryBean实例化
-
步骤一:添加依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.9.RELEASE</version> </dependency> <!--Mybatis和Spring整合--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.17.RELEASE</version> </dependency> <!--mysql连接数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> </dependencies>
-
步骤二:创建MybatisConfig配置类
@Configuration public class MybatisConfig { /** * 创建DruidDataSource方法 */ @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(“com.mysql.jdbc.Driver”); dataSource.setUrl(“jdbc:mysql://localhost:3306/spring_db”); dataSource.setUsername(“root”); dataSource.setPassword(“root”); return dataSource; } @Bean("sqlSessionFactory") public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } }
说明:
-
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) 会从Spring容器中获取
DataSource
-
-
测试:SqlSessionFactory
@Test public void testSqlSessionFactoryByAnnotation() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class); SqlSessionFactory sqlSessionFactory = context.getBean("sqlSessionFactory", SqlSessionFactory.class); System.out.println("sqlSessionFactory = " + sqlSessionFactory); }
-
-
场景2:MybatisConfig是一个Spring配合类,SpringConfig业也是一个配置类,怎么合并在一起呢?
-
注解:@Import 等价于applicationContext.xml中的
-
@Import代码演示:
@Configuration @ComponentScan(basePackages = {"com.itheima.dao", "com.itheima.service", "com.itheima.controller"}) @Import({MybatisConfig.class}) public class SpringConfig { }
说明:这里的SpringConfig也可以通过@ComponentScan(“com.itheima”)一步到位扫描到了MybatisConfig。
二、注解依赖注入
1.@Value注解数值
-
场景:如下代码硬编码在程序中,不太好,如何使用注解对数值进行分离?
@Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(“com.mysql.jdbc.Driver”); dataSource.setUrl(“jdbc:mysql://localhost:3306/spring_db”); dataSource.setUsername(“root”); dataSource.setPassword(“root”); return dataSource; }
-
注解:@Value 既可以给成员变量赋值,也可以通过@PropertySource加载properties文件时读取数据
@Value(“root”) Private String username;
@PropertySource(value = "classpath:jdbc.properties") public class ClassName { @Value("${jdbc.username}") private String userName; } 注意: 1.@PropertySource等价<context:property-placeholder location="classpath:jdbc.properties"/> 2.@PropertySource不能使用通配符,例如:@PropertySource(value = "classpath:**/*.properties")是不允许的 3.@Value等价<property name value="${jdbc.username}" />
-
代码演示:对SqlSessionFactoryBean中DataSource赋值分离
@Configuration @PropertySource("classpath:jdbc.properties") public class MybatisConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 创建DruidDataSource方法 */ @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } @Bean("sqlSessionFactory") public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); return sqlSessionFactoryBean; } }
2.@Autowired注入引用类型
-
场景:@Value可以给类中成员赋值普通类型数值,如果成员是一个引用类型,该如何赋值呢,也就是依赖注入呢?
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { private UserDao userDao; }
-
注解:@Autowried作用于方法、构造函数、方法列表、成员,@ComponentScan开关可以让@Autowired发挥作用
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. * <p>Defaults to {@code true}. */ boolean required() default true; } @Autowired默认按类型装配,如果有多个类型会按照名称去找。 1.若容器中只有此类型的一个对象 , 就直接注入 2.若容器中存在此类型的多个对象 , 会把字段或者参数名字作为bean的id去容器中查找 3.若找到 , 直接注入 ; 若没有找到 , 报异常
@Autowired private UserDao userDao; @Autowired public UserServiceImpl(UserDao userDao) { this.userDao = userDao; } @Autowired public void aa(UserDao userDao) { this.userDao = userDao; }
-
代码演示@Autowired按照类型找
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public void save() { userDao.save(); System.out.println("UserServiceImpl.save"); } }
@Repository("userDao") public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("UserDaoImpl.save"); } }
-
代码演示@Autowired按照名字找
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { @Autowired private UserDao userDaoB; @Override public void save() { userDao.save(); System.out.println("UserServiceImpl.save"); } }
@Repository("userDao") public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("UserDaoImpl.save"); } }
@Repository("userDaoB") public class UserDaoImplB implements UserDao { @Override public void save() { System.out.println("UserDaoImplB.save"); } }
测试: 存在UserDao2个类型 ,会根据userDaoB来找
-
按照名字找,与@Autowired连用的还有一个注解@Qualifier(“userDaoB”)
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { @Autowired @Qualifier("userDaoB") private UserDao userDao; }
-
按照名字找,也可以使用 @Resource
@Service("userService") @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) @Lazy(false) public class UserServiceImpl implements UserService { @Resource private UserDao userDao; }
-
三、Spring整合
1.Spring整合Mybatis框架
-
Mybatis框架操作数据实现步骤回顾
-
步骤一: Mybatis的依赖
<dependencies> <!--spring和mybatis整合--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!--连接数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <!--测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
-
步骤二: Mybatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="jdbc.properties"/> <!--声明别名--> <typeAliases> <package name="com.itheima.domain"/> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <package name="com.itheima.dao"/> </mappers> </configuration>
-
步骤三: Mybatis接口定义
public class User { private Long id; private String name; private Integer age; private String address; //这里省略了set、get方法 }
create table user ( id bigint(50) auto_increment primary key, name varchar(255) null, age int null, address varchar(255) null ) charset = utf8mb4;
public interface UserMapper { @Select("select * from user where id=#{id}") public User selectById(@Param("id") Long id); @Insert("insert into user(name,age,address) values(#{name},#{age},#{address})") public void insertUser(User user); }
-
步骤四: 测试Mybatis获取接口代理对象操作数据库
public class MybatisTest { @Test public void testSelectByMybatis() throws IOException { //1. SqlSessionFactory SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); //2. SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.selectById(1453642066977423362L); System.out.println("user = " + user); } @Test public void testInsertByMybatis() throws IOException { //1. SqlSessionFactory SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); //2. SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); //3. 获取接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = new User(); user.setName("彭总"); user.setAge(89); user.setAddress("湖南"); userMapper.insertUser(user); sqlSession.commit(); } }
-
-
Spring可以管理Mybatis哪些对象,以达到解耦呢?
-
SqlSessionFactory对象: SqlSessionFactoryBean替代
-
数据源对象: DruidDataSource
-
接口代理对象: MapperScannerConfigurer
-
-
Spring和Mybatis实现
-
步骤一:添加依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.17.RELEASE</version> </dependency>
-
步骤二:创建MybatisConfig配置类
@Configuration @PropertySource("classpath:jdbc.properties") @MapperScan(basePackages = "com.itheima.dao") public class MybatisConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * 创建DruidDataSource方法 */ @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } @Bean("sqlSessionFactory") public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setTypeAliasesPackage("com.itheima.domain"); return sqlSessionFactoryBean; } //可以使用@MapperScan(basePackages = "com.itheima.dao")等价 //@Bean //public static MapperScannerConfigurer mapperScannerConfigurer() { // MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); // mapperScannerConfigurer.setBasePackage("com.itheima.dao"); // return mapperScannerConfigurer; //} }
-
步骤三:测试
public class SpringTest { @Test public void testSelectBySpringMybatis() { //1. 注解容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class); //2. 从容器中获取UserMapper接口代理对象 UserMapper userMapper = context.getBean(UserMapper.class); User user = userMapper.selectById(1453642066977423362L); System.out.println("user = " + user); } @Test public void testInsertBySpringMybatis() { //1. 注解容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MybatisConfig.class); //2. 从容器中获取UserMapper接口代理对象 UserMapper userMapper = context.getBean(UserMapper.class); User user = new User(); user.setName("小周2"); user.setAge(18); user.setAddress("武汉"); userMapper.insertUser(user); } }
-
2.Spring整合Junit测试
-
场景:上例中每次都通过AnnotationConfigApplicationContext从Spring容器获取bean,这样好么?
-
分析:
- 每一个测试用例中多个测试方法都需要初始化一次容器,导致容器重复初始化多次,影响了测试性能
- 手动硬编码通过getBean()方法从spring容器中获取bean
-
解决:Spring和Junit测试整合
- 个测试用例中不管多少个测试方法,容器只初始化一次
- 获取bean,只需要自动装配赋值即可
-
代码实现演示
-
添加依赖
<!--测试依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.17.RELEASE</version> </dependency>
-
测试:@RunWith使用什么Runner来完成测试方法,@ContextConfiguration加载配置什么文件
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = MybatisConfig.class) public class SpringTest { @Autowired UserMapper userMapper; @Test public void testSelectBySpringMybatis() { User user = userMapper.selectById(1453642066977423362L); System.out.println("user = " + user); } @Test public void testInsertBySpringMybatis() { User user = new User(); user.setName("小周2"); user.setAge(18); user.setAddress("武汉"); userMapper.insertUser(user); } }
说明:
- 从Spring5.0以后,要求Junit的版本必须是4.12及以上
- Junit仅用于单元测试,不用将Junit的测试类配置成spring的bean
-
总结
-
实例化bean
- @Component: @Controller、@Service、@Repository、@Configuration
- @Bean
-
依赖注入
- @PropertySource(“classpath:jdbc.properties”) @Value("${}")
- @Autowired 首先按照类型去找,没有找到再按照名称去找
-
Spring整合
-
与Mybatis整合关键
SqlSessionFactoryBean---->获取SqlSessionFactory MapperScannerConfigurer--->扫描接口,产生代理对象,等价@MapperScan(basePackages = "")
-
与Junit整合关键
@Runwith(SpringRunner.class) @ContextConfiguration(classes=xx)
-