目录
注解定义bean
在实现类使用注解@Component
注意Spring提供了@Component注解的三个衍生注解
纯注解开发
bean管理
bean的作用范围
bean的生命周期
bean的依赖注入
引用类型的注入
如果有两个实现类呢(即有两个相同变量类型的bean),那该怎么办?
简单类型的注入
配置第三方Bean
获取Bean
抽取出新的配置类
注解定义bean
在实现类使用注解@Component
@Component
public class BookDaoImpl implements BookDao {public void update(){System.out.println("BookDao");}
}
那么spring怎么知道它要被定义成bean,所以在springConfig.xml文件中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
"><context:component-scan base-package="com.hhh.dao"></context:component-scan>
</beans>
使用<context:component-scan>标签指定扫描包
public class App {public static void main(String[] args) {//加载配置文件,初始化容器ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");//容器中获取beanBookDao bookDao = ctx.getBean(BookDao.class);//或者BookDao bookDao = ctx.getBean(BookDaoImpl.class);bookDao.update();}
}
除了可以通过类.class的方式来获取bean还可以通过id来获取bean,我们没有在<bean>标签中定义id名,那么对应的bean对象的id名是什么呢?
如果实现类的命名是驼峰式,那么id名就是开头字母小写,如果不是驼峰式,类名就是id名
public class App {public static void main(String[] args) {//加载配置文件,初始化容器ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");//容器中获取bean//BookDao bookDao = ctx.getBean(BookDao.class);//或者BookDao bookDao = ctx.getBean(BookDaoImpl.class);BookDao bookDao= (BookDao) ctx.getBean("bookDaoImpl");bookDao.update();}
}
我们也可以直接使用注解@Component的value属性来赋值,值就是id名
@Component("bookDao1")
public class BookDaoImpl implements BookDao {public void update(){System.out.println("BookDao");}
}
public class App {public static void main(String[] args) {//加载配置文件,初始化容器ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");//容器中获取bean//BookDao bookDao = ctx.getBean(BookDao.class);//或者BookDao bookDao = ctx.getBean(BookDaoImpl.class);//BookDao bookDao= (BookDao) ctx.getBean("bookDaoImpl");BookDao bookDao= (BookDao) ctx.getBean("bookDao1");bookDao.update();}
}
注意Spring提供了@Component注解的三个衍生注解
- @Controller:用于表现层(web)bean的定义
- @Service:用于业务层(service)bean的定义
- @Repository:用于数据层(dao)bean的定义
@Repository("bookDao1")
public class BookDaoImpl implements BookDao {public void update(){System.out.println("BookDao");}
}
可以发现@Repository其实和@Component注解的功能基本一样,但是这样把三层架构的注解区别,更美观,也更有利于管理
纯注解开发
spring3.0升级了纯注解开发,使用java类代替配置文件。
@Configuration
@ComponentScan("com.hhh.dao")
public class SpringConfig {
}
使用两个注解
@Configuration说明这个类是配置文件
@ComponentScan说明这个配置文件要扫描的包
使用(先把配置文件删除):
public class App {public static void main(String[] args) {//加载配置文件,初始化容器//ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");//不使用配置文件初始化容器,加载配置类ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);//容器中获取bean//BookDao bookDao = ctx.getBean(BookDao.class);//或者BookDao bookDao = ctx.getBean(BookDaoImpl.class);//BookDao bookDao= (BookDao) ctx.getBean("bookDaoImpl");BookDao bookDao= (BookDao) ctx.getBean("bookDao1");bookDao.update();}
}
那如果要扫描两个包呢?
@Configuration
@ComponentScan({"com.hhh.dao","com.hhh.service"})
public class SpringConfig {
}
bean管理
bean的作用范围
之前我们需要在<bean>标签中的scope属性来配置,现在我们只需要在实现类的开头使用注解@Scope即可
@Repository("bookDao1")
@Scope("prototype")
//多例
public class BookDaoImpl implements BookDao {public void update(){System.out.println("BookDao");}
}
bean的生命周期
在配置文件中我们需要使用init-method,destroy-method来配置初始方法和销毁方法
现在我们只需要用 @PostConstruct定义初始方法,@PreDestroy定义销毁方法
@Repository("bookDao1")
//@Scope("prototype")
public class BookDaoImpl implements BookDao {public void update(){System.out.println("BookDao");}@PostConstructpublic void init(){System.out.println("init");}@PreDestroypublic void destroy(){System.out.println("destroy");}
}
如果这两个注解失效,是因为@PostConstruct 和 @PostDestroy
注释都是Java EE的一部分。而且由于Java EE在Java 9中已被弃用,而在Java 11中已被删除
所以只需要在pom.xml中加上
<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version></dependency>
使用
public class App {public static void main(String[] args) {//加载配置文件,初始化容器//ApplicationContext ctx=new ClassPathXmlApplicationContext("springConfig.xml");//不使用配置文件初始化容器AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);//容器中获取bean//BookDao bookDao = ctx.getBean(BookDao.class);//或者BookDao bookDao = ctx.getBean(BookDaoImpl.class);//BookDao bookDao= (BookDao) ctx.getBean("bookDaoImpl");BookDao bookDao= (BookDao) ctx.getBean("bookDao1");bookDao.update();ctx.close();//bookDao.update();}
}
结果:
bean的依赖注入
service层
引用类型的注入
@Service
public class BookServiceImpl implements BookService {@Autowired//根据变量类型自动依赖注入private BookDao bookDao;public void update(){bookDao.update();System.out.println("BookService");}
}
使用注解@AutoWired即可实现依赖注入,原理是在容器中寻找有没有与这个变量类型一致的bean对象,如果有就直接依赖注入。
可以发现使用注解不用写setter方法,因为直接使用的暴力反射进行赋值
如果有两个实现类呢(即有两个相同变量类型的bean),那该怎么办?
不处理就会报错
No qualifying bean of type 'com.hhh.dao.BookDao' available: expected single matching bean but found 2: bookDao1,bookDaoImpl2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
如果有和变量名一致的id(前提:类型一致)那么就可以直接赋值,不会报错
但是使用注解@Qualifier可以直接指定使用哪个bean
注意@Qualifier不可单独使用,必须和@AutoWired一起用
@Service
public class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao2")//使用id名为bookDao2的实现类对象//根据变量类型自动依赖注入private BookDao bookDao;public void update(){bookDao.update();System.out.println("BookService");}
}
结果:
简单类型的注入
使用注解@Value
@Service
public class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao2")//根据变量类型自动依赖注入private BookDao bookDao;@Value("hhh")private String name;@Value("foo,bar,fun")private List<String> list;public void update(){bookDao.update();System.out.println("BookService");System.out.println("name="+name);System.out.println(list);}
}
注入外部文件
现在SpringConfig类中添加注解@PropertySource
@Configuration
@ComponentScan({"com.hhh.dao","com.hhh.service"})
@PropertySource("classpath:jdbc.properties")//加载名字为dbc.properties的文件
public class SpringConfig {
}
这是文件的内容
@Service
public class BookServiceImpl implements BookService {@Autowired@Qualifier("bookDao2")//根据变量类型自动依赖注入private BookDao bookDao;@Value("hhh")private String name;@Value("foo,bar,fun")private List<String> list;@Value("${jdbc.username}")//去加载文件的key为username的value值private String nm;public void update(){bookDao.update();System.out.println("BookService");System.out.println("name="+name);System.out.println(list);System.out.println(nm);}
}
配置第三方Bean
先导入坐标
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency>
@Configuration
@ComponentScan({"com.hhh.dao","com.hhh.service"})
@PropertySource("jdbc.properties")
public class SpringConfig {@Beanpublic DataSource getDataSource(){DruidDataSource druidDataSource = new DruidDataSource();return druidDataSource;}
}
使用注解@Bean让这个方法的返回值变成Bean(交给Spring容器管理),默认id为方法名
获取Bean
public class App {public static void main(String[] args) {//不使用配置文件初始化容器AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);//第一种方法://DataSource dataSource = ctx.getBean(DataSource.class);//使用类//第二种方法:使用id名,id名为方法名Object dataSource = ctx.getBean("getDataSource");System.out.println(dataSource);}
}
或者直接使用@Bean的属性直接配置id名,这样一来id就不是方法名了
@Configuration
@ComponentScan({"com.hhh.dao","com.hhh.service"})
@PropertySource("jdbc.properties")
public class SpringConfig {@Bean("dataSource")public DataSource getDataSource(){DruidDataSource druidDataSource = new DruidDataSource();return druidDataSource;}
}
抽取出新的配置类
可以发现这是数据源的配置文件,可以把它从Spring的配置文件中抽取出来
然后再SpringConfig类中使用@Import注解导入该文件即可
@Configuration
@ComponentScan({"com.hhh.dao","com.hhh.service"})
@PropertySource("jdbc.properties")
@Import({jdbcConfig.class})
public class SpringConfig {}
或者
直接再jdbcConfig类中直接用@Configuration注解,但是SpringConfig类的@ComponentScan要改成整个包,保证扫描到jdbcConfig类
配置数据库的四个参数
public class jdbcConfig {@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String name;@Value("${jdbc.driver}")private String driver;@Value("${jdbc.password}")private String password;@Bean("dataSource")public DataSource getDataSource(){System.out.println("driver="+driver);DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl(url);druidDataSource.setDriverClassName(driver);druidDataSource.setName(name);druidDataSource.setPassword(password);return druidDataSource;}
}
使用的是SpringConfig导入了jdbc.properties文件
如果需要使用引用数据类型,直接再方法里的参数里写,它就会自己注入,不需要@Autowire注解