2、spring的第二种注入方法:基于构造方法完成对属性值的注入
<bean id="user" class="com.jy.beans.User">
<!--通过构造注入数据-->
<constructor-arg name="name" value="dnflaksd"></constructor-arg>
<constructor-arg name="date" ref="date"></constructor-arg>
</bean>
<bean id="date" class="java.util.Date"></bean>
七、关于组件的生命周期
a.组件的作用范围是单例:组件会随着容器的销毁而销毁。
b.组件的作用范围是多例:容器销毁时,组件不会销毁,会由垃圾回收机制回收。
八、模拟spring底层基于工厂模式解耦
首先程序会读取配置文件,获取文件中的全类名数据,根据反射创建对象(单例),保存在容器中,后面如果有用到实例的地方,我们从容器中获取。
beans.properties
caseServlet = com.jy.factory.servlet.CaseServlet
caseService = com.jy.factory.service.CaseServiceImpl
caseDao = com.jy.factory.dao.CaseDaoImpl
BeanFactory:
public class BeanFactory {
/**
* 模拟一个容器
*/
private static Map beansMap;
private static Properties properties;
/**
* 在静态代码块中创建容器对象,是为了保证存入容器中的实例是单例的,所有的存放实例的步骤,
* 也要在类加载的时候完成
*/
static {
//实例化容器
beansMap = new HashMap();
//实例化properties集合
properties = new Properties();
FileReader fileReader = null;
//读取配置文件
try {
fileReader = new FileReader("src/beans.properties");
//读取数据到集合
properties.load(fileReader);
//获取properties中所有的key
Enumeration keys = properties.keys();
//遍历所有的key
while (keys.hasMoreElements()){
//先获取对应key的字符串
String s = keys.nextElement().toString();
//根据key获取value
String propertyClass = properties.getProperty(s);
//根据反射创建对象
Object value = Class.forName(propertyClass).newInstance();
//将实例存到容器里
beansMap.put(s,value);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 定义一个获取实例的方法
*/
public static Object getBean(String beanName){
//从集合中获取实例
Object o = beansMap.get(beanName);
return o;
}
}
持久层:CaseDaoImpl
public class CaseDaoImpl implements CaseDao{
@Override
public void getMethod() {
System.out.println("持久层数据");
}
}
业务层:CaseServiceImpl
public class CaseServiceImpl implements CaseService{
@Override
public void getMethod() {
CaseDao caseDao = (CaseDao)BeanFactory.getBean("caseDao");
caseDao.getMethod();
}
}
表现层:CaseServlet
public class CaseServlet {
public void getMethod(){
/**
* 从工厂中获取实例
*/
CaseService caseService = (CaseService)BeanFactory.getBean("caseService");
caseService.getMethod();
}
}
测试:
public static void main(String[] args) {
//从自定义工厂对象中获取表现层实例
CaseServlet caseServlet = (CaseServlet)BeanFactory.getBean("caseServlet");
caseServlet.getMethod();
}
九、基于注解的IOC配置
1、在配置文件上开启spring对注解的支持
<!--
开启spring对注解的支持:其实就是一个扫描包的过程
spring程序在执行的时候会解析配置文件的数据,找到对应的包结构,并扫描该包下面的子包,
然后回去检测每个类上有没有spring提供的注解,如果有,会将该类的实例装载进spring容器中
-->
<context:component-scan base-package="com.jy"></context:component-scan>
2、在类上加上对应的注解
四个注解作用一样:
@Component:把资源让spring来管理,相当于在xml中配置一个bean
@Controller:一般用于表现层的注解
@Service:一般用于业务层的注解
@Repository:一般用于持久层的注解
@Component
public class Student {
}
/**
*核心容器对象获取实例时,其标识(参数)默认为首字母小写的类名
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/beans2.xml");
Object user = classPathXmlApplicationContext.getBean("student");
System.out.println(user);
}
四个注解都可以自定义标识
@Component("sss")
public class Student {
}
/**
*核心容器对象获取实例时,其标识(参数)默认为首字母小写的类名
*/
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("xml/beans2.xml");
Object user = classPathXmlApplicationContext.getBean("sss");
System.out.println(user);
}
@Scope:可以改变bean的作用范围
如:@Scope(“prototype”)
生命周期相关:
@PostConstruct:用于指定初始化的方法
@PreDestroy:用于指定销毁的方法
@Component
@Scope("prototype")
public class Student {
@PostConstruct
public void initMethod(){
System.out.println("初始化的方法");
}
@PreDestroy
public void destroyMethod(){
System.out.println("销毁的方法");
}
}
用于注入数据的注解:
@Autowired(根据类型注入):
注入流程:spring扫描到属性上有该注解后,会去容器中找到该属性对应的类型与之注入。
如果spring容器中与该属性类型匹配的有多个,比如一个接口有多个实现类,实现类都采用了注解的默认值,那么究竟该注入哪个实例?
这种情况下程序会报错!
解决方案:
1、给这个类起标识, 标识要和被注入属性的属性名相同。
2、与Qualifier结合使用,由Qualifier来决定注入哪个实例。
@Qualifier(不能单独使用):只能和Autowired结合使用
@Resource(根据id注入的)
@Value(用于注入基本数据类型和String类型):支持el表达式。不能直接注入静态属性。