容器(可以用来管理所有的组件(类))
核心关注:IOC和AOP
1.IOC
Inversion(反转) Of Control:控制反转
控制:资源的获取方式
1.主动式(要什么资源自己创建)
Person{
Book book=new Book();
Dog dog=new Dog();
//复杂对象的创建时比较庞大的工程
}
2.被动式:资源的获取不是自己创建,而是交给一个容器创建和设置
Person{
Book book;
public void test(){
book.read();
}
}
容器:管理所有的组件(有功能的类),主动的new资源改为被动的接受资源
1.1 DI(Dependency Injection)依赖注入
容器能知道哪个组件(类)运行的时候,需要另外一个组件(类);
容器通过反射的形式,将容器中准备好的Book对象注入(利用反射给属性赋值)到Person中
IOC只是思想,而DI是具体的实现
代码实现:
1.实体类
public class Person {
private String name;
private Integer age;
private String gender;
private String email;
public Person() {
System.out.println(\"person的构造器!\");
}
public void setName(String name) {
System.out.println(\"设置pserson的name\");
this.name = name;
}
public void setAge(Integer age) {
System.out.println(\"设置person的age\");
this.age = age;
}
public void setGender(String gender) {
System.out.println(\"设置person的gender\");
this.gender = gender;
}
public void setEmail(String email) {
System.out.println(\"设置person的email\");
this.email = email;
}
....
...get()
}
2.spring的配置文件ioc.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\"
xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">
<!--注册person对象,spring会自动创建这个person对象-->
<bean class=\"com.Person\" id=\"person01\">
<property name=\"age\" value=\"18\"></property>------------->name是bean中的属性,通过set方法反射注入
<property name=\"email\" value=\"244594537@qq.com\"/>
<property name=\"gender\" value=\"男\"/>
<property name=\"name\" value=\"吴孟达\"/>
</bean>
</beans>
3.测试类:
public class Test {
public static void main(String[] args) {
System.out.println(\"启动spring容器....\");
ApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");--------->启动spring的配置文件
System.out.println(\"spring容器启动成功!\");
Person person= (Person) ioc.getBean(\"person01\");----------->此处的person01为spring配置文件中的bean的id
System.out.println(person);
}
}
输出:
启动spring容器....
person的构造器!
设置person的age
设置person的email
设置person的gender
设置pserson的name
spring容器启动成功!
Person{name=\'吴孟达\', age=18, gender=\'男\', email=\'244594537@qq.com\'}
结论:------>发现其执行顺序为:
1.<bean...>元素驱动spring容器调用构造器创建对象
2.<property...>元素驱动spring执行setter方法
如果一个实体类中引用了其他实体类,容器加载的执行顺序
1.第一种情况:范围大的(person引用book)在范围小的前面
spring配置文件内容:
<bean id=\"person01\" class=\"entity.Person\">
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
<property name=\"book\" ref=\"book\"/>
</bean>
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
实体类信息:
。。。
测试类信息:
public static void main(String[] args) {
System.out.println(\"加载spring....\");
ApplicationContext ac=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
Person person= (Person) ac.getBean(\"person01\");
System.out.println(person.toString());
}
输出:
加载spring....
person实例化!
Book实例化!
Book执行set name方法
Book执行set price方法
person执行set age方法
person执行set name方法
spring容器启动成功!
发现执行顺序为:
1.先实例化两个对象
2.在执行小的set方法
3.再执行大的set方法
第二种情况:小范围的在上
spring配置文件内容:
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
<bean id=\"person01\" class=\"entity.Person\">
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
<property name=\"book\" ref=\"book\"/>
</bean>
输出:
Book实例化!
Book执行set name方法
Book执行set price方法
person实例化!
person执行set age方法
person执行set name方法
spring容器启动成功!
执行顺序为:
1.小范围对象实例化
2.小范围对象set方法
3.大范围对象实例化
4.大范围对象set方法
2.源码解析
1.
以此为示例:
<bean id=\"book\" class=\"entity.Book\"></bean>
实际上<bean.../>元素默认一反射的方式来调用该类的无参构造器
底层简单源码如下:
String idStr=...;//解析<bean。。。。/>元素的id属性得到该字段的字符串值为\"book\"
String classStr=...;//解析class属性得到该字段的值为:entity.Book
Class clazz=Class.forName(classStr);
Object object=clazz.newInstance();//通过反射示例化对象
container.put(idstr,obj);//将对象放入容器给中,container为spring容器
2.
<bean id=\"person01\" class=\"entity.Person\">
<property name=\"book\" ref=\"book\"/>
</bean>
底层的简单源码如下:
String nameStr=...;解析<property.../>元素的name属性得到该字符串的值为book
String refStr=..;解析<property.../>元素的ref属性得到该字符串的值为book
String setterName-\"set\"+nameStr.subString(0,1).toUpperCase()+name.subString(1);//生成将要调用的setter方法】
Object paramBean=container.get(refStr);//从容器中取到refStr的bean,作为传入参数
Method setter=clazz.getMethod(setterName,parmBean.getClass())//此处的clazz和1的对应起来
setter.incoke(obj,parmBean);//此处的obj和1的对应起来
3.组件在spring容器中是单例的
public static void main(String[] args) {
System.out.println(\"加载spring....\");
ApplicationContext ac=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
Person person1= (Person) ac.getBean(\"person01\");
Person person2= (Person) ac.getBean(\"person01\");
System.out.println(person1==person2);------------------------->此时输出为true;
}
4.使用构造器为bean的属性赋值
spring配置文件为:
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
<bean id=\"person01\" class=\"entity.Person\">-------------------------------->此处有两个person的bean:这一个使用set方法给属性赋值,调用的是无参构造器
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
<property name=\"book\" ref=\"book\"/>
</bean>
<bean id=\"person02\" class=\"entity.Person\">------------------------------>这里调用的是有参构造器来进行属性赋值
<constructor-arg name=\"age\" value=\"18\"></constructor-arg>
<constructor-arg name=\"book\" ref=\"book\"></constructor-arg>
<constructor-arg name=\"name\" value=\"吴孟达02\"></constructor-arg>
</bean>
person类的代码:
public class Person {
private String name;
private Integer age;
private Book book;
public Person() {
System.out.println(\"person执行无参构造器\");
}
public Person(String name, Integer age, Book book) {
this.name = name;
this.age = age;
this.book = book;
System.out.println(\"person执行有参构造器\");
}
get/set方法
}
测试类方法:
public static void main(String[] args) {
System.out.println(\"加载spring....\");
ApplicationContext ac=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
Person person= (Person) ac.getBean(\"person02\");
System.out.println(person.toString());
}
输出:
加载spring...
Book实例化!
Book执行set name方法
Book执行set price方法
person执行无参构造器-------------->调用无参构造器实例化对象,然后调用set方法赋值
person执行set age方法
person执行set name方法
person执行有参构造器-------------->调用有参构造器,并且直接赋值
spring容器启动成功!
Person{name=\'吴孟达02\', age=18, book=Book{name=\'java分析\', price=32}}
5.使用p名称空间为bean属性赋值
1.在spring的xml文件中加入这一句:xmlns:p=\"http://www.springframework.org/schema/p\"
<?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:p=\"http://www.springframework.org/schema/p\"----------------------------------->加入这一句
xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
<bean id=\"person03\" class=\"entity.Person\" p:age=\"18\" p:name=\"吴孟达03\" p:book-ref=\"book\"></bean>------>此时可以通过p标签进行赋值
</beans>
6.复杂赋值
1.给属性赋值null
<bean id=\"person04\" class=\"entity.Person\">
<property name=\"name\">
<null></null>---------------------------->使用null标签进行赋值:不能使用<property name=\"name\" value=\"null\">这是付了一个null的字符串
</property>
</bean>
2.属性是引用时
2.1引用外部bean
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
<bean id=\"person04\" class=\"entity.Person\">
<property name=\"name\">
<null></null>
</property>
<property name=\"book\" ref=\"book\"></property>------------->如果外边已经有了像引用的Book bean,则使用ref引用:这里意思是:book=ioc.getBean(\"book\")
</bean>
测试代码:
ApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
Person person= (Person) ioc.getBean(\"person04\");
System.out.println(ioc.getBean(\"book\")==person.getBook());------------->此时输出为true
2.2内部引用
<bean id=\"person04\" class=\"entity.Person\">
<property name=\"name\">
<null></null>
</property>
<property name=\"book\">
<!--对象我们可以使用bean标签创建 book=new Book();引用内部bean-->
<bean class=\"entity.Book\">---------------------------------------->此处需要注意的是:内部bean不能直接通过ioc容器获取:
<property name=\"name\" value=\"java\"></property> ----->如<bean id=\"bookInner\" class=\"entity.Book\">内部bean加上id
<property name=\"price\" value=\"25\"></property> ------>ioc.getBean(\"bookInner\")会获取出错!
</bean>
</property>
</bean>
测试代码为:
ApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
Person person= (Person) ioc.getBean(\"person04\");
System.out.println(ioc.getBean(\"book\")==person.getBook());------------->此时输出为false
3.为list属性赋值
为psrson新增属性
private List<Book> library;
如何为library赋值
<property name=\"library\">
<!--library=new ArrayLiast<Book>-->
<list>-------------------------------->使用过list标签
<bean class=\"entity.Book\" p:name=\"java\" p:price=\"14\"></bean>------>1.用bean标签创建list元素
<ref bean=\"book\"></ref>-------------------------------------------->2.用ref标签引入外部bean
</list>
</property>
4.为map赋值
为person新增一个属性
private Map map;
springxml中的配置
<property name=\"map\">
<map>-------------------------------------------->使用map标签:map=new HashMap<>();
<entry key=\"key01\" value=\"张三\"></entry>
<entry key=\"key02\" value=\"18\"></entry>
<entry key=\"book01\" value-ref=\"book\"></entry>----->可以使用value-ref引入外部bean
<entry key=\"key04\">
<bean class=\"entity.Person\" p:name=\"吴孟达\" p:age=\"18\" p:book-ref=\"book\"></bean>------>也可以使用该方式引入内部bean
</entry>
<entry key=\"key05\">---->map中嵌套map
<map>
</map>
</entry>
</map>
</property>
5.为Properties赋值
person新增一个属性:
private Properties properties;
spring的配置文件中:
<property name=\"properties\">
<!--properties=new Properties();所有的k=v都是String-->
<props>
<!--k=v都是string,值直接写在标签中-->
<prop key=\"username\">root</prop>
<prop key=\"password\">123456</prop>
</props>
</property>
6.使用util名称空间创建集合类型的bean
使用场景:如果相同的map或者list在多处都有引用
可以将map或list单独拿出来做个bean
使用步骤
1.在spring的配置文件中加入:xmlns:util=\"http://www.springframework.org/schema/util\"
<?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:p=\"http://www.springframework.org/schema/p\"
xmlns:util=\"http://www.springframework.org/schema/util\"---------------------->在spring的配置文件中加入这行
xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\">
。。。。。
</bean>
2.
<!--相当于new LinkedHashMap<>()-->
<util:map id=\"mymap\">
<!--往map中添加元素-->
<entry key=\"key01\" value=\"张三\"></entry>
<entry key=\"key02\" value=\"18\"></entry>
<entry key=\"book01\" value-ref=\"book\"></entry>
<entry key=\"key04\">
<bean class=\"entity.Person\" p:name=\"吴孟达\" p:age=\"18\" p:book-ref=\"book\"></bean>
</entry>
<entry key=\"key05\">
<map></map>
</entry>
</util:map>
3.其他地方的使用
<property name=\"map\" ref=\"mymap\"></property>----->直接根据引用获取即可
也可以在代码中直接获取
Map<String,Object> map= (Map<String, Object>) ioc.getBean(\"mymap\");
7.util:list的使用和list标签类似
<util:list id=\"mylist\">
<bean class=\"entity.Person\" p:book=\"西游\" p:name=\"吴孟达\"></bean>
<ref bean=\"mymap\"></ref>
<value>12</value>
</util:list>
8.级联属性:属性的属性
<bean id=\"book\" class=\"entity.Book\">
<property name=\"name\" value=\"java分析\"/>
<property name=\"price\" value=\"32\"/>
</bean>
<bean id=\"person05\" class=\"entity.Person\">
<property name=\"book\" ref=\"book\"></property>
<property name=\"book.price\" value=\"1000\"></property>
----->这里通过book.price直接更改:person的book属性的price属性:但这里注意的是这里一改,容器中的book的bean的price属性改为1000
</bean>
9.通过继承实现bean属性的重用
<bean id=\"person01\" class=\"entity.Person\">
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
<property name=\"book\" ref=\"book\"/>
</bean>
这里需要一个personbean,其他属性都一样,只有age属性变为19,则可以这样
<bean id=\"person06\" class=\"entity.Person\" parent=\"person01\">--------->使用parent属性,指定需要继承属性的bean id,这里的继承只是当前bean的配置信息继承,并不是真正的类继承
<property name=\"name\" value=\"刘丹\"></property>
</bean>
结论:
1. 这里的person01和pserson06在容器中是不同的组件(对象)
2.这两个组件的属性都相同,只有name属性值不同
3.因为指定了要继承配置信息的类,所以上述还可以这样写
<bean id=\"person06\" parent=\"person01\">-------------------------->省略了class,因为配置信息继承于person01,所以class配置值可以继承person01的class配置值值
<property name=\"name\" value=\"刘丹\"></property>
</bean>
4.父类的信息不会因为子类而更改!
10.专门建立一个供其他bean继承的bean
<bean id=\"person01\" class=\"entity.Person\" abstract=\"true\">----------------------->加入:abstract=\"true\"
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
<property name=\"book\" ref=\"book\"/>
</bean>
abstract=\"true\"这个bean的配置是一个抽象的,不能获取他的实例,只能被别人继承
此时:
ioc.getBean(\"person01\");-------------------->此时获取会报错,因为这个是被其他bean继承的
7.bean的作用域
1.单例:scope=\"singleton\"
<bean id=\"person05\" class=\"entity.Person\" scope=\"singleton\">
<property name=\"book\" ref=\"book\"></property>
<property name=\"book.price\" value=\"1000\"></property>
</bean>
2.多例:scope=\"prototype\"
<bean id=\"person05\" class=\"entity.Person\" scope=\"prototype\">
<property name=\"book\" ref=\"book\"></property>
<property name=\"book.price\" value=\"1000\"></property>
</bean>
结论:
1.scope=\"singleton\"单例模式:默认
1.1在容器启动完成前就已经创建好对象,保存在容器中
1.2任何获取都是获取之前创建好的对象
2.scope=\"prototype\"多例模式
2.1容器启动默认不会创建多例的bean
2.2每次获取的时候创建这个bean(ioc.getBean(\"person05\"))
2.3每次获取都会创建一个新的对象
8.bean的生命周期(自定义初始化方法和销毁方法)
1.当是单例模式
1.person实体类
public class Person {
//person的无参构造器
public Person() {
System.out.println(\"person的无参构造器方法...\");
}
//自定义初始化方法
public void initMethod(){
System.out.println(\"person的初始化方法\");
}
//自定义对象销毁方法
public void destroyMethod(){
System.out.println(\"person的销毁方法\");
}
}
2.spring的配置文件
<bean id=\"person\" class=\"entity.Person\"
init-method=\"initMethod\"--------------------------->指定自定义的初始化方法
destroy-method=\"destroyMethod\"--------------------->指定自定义的销毁方法
>
</bean>
3.测试类
public static void main(String[] args) {
System.out.println(\"spring容器启动...\");
ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
System.out.println(\"关闭spring容器...\");
ioc.close();---------------------------------------->调用容器的停止方法
System.out.println(\"关闭spring容器成功!\");
}
输出:
spring容器启动...
person的无参构造器方法...
person的初始化方法
spring容器启动成功!
关闭spring容器...
person的销毁方法
关闭spring容器成功!
2.当是多例模式
2.1ioc的配置文件
<bean id=\"person\" class=\"entity.Person\"
scope=\"prototype\"---------------------------->多例模式
init-method=\"initMethod\"
destroy-method=\"destroyMethod\"
>
</bean>
测试代码:
public static void main(String[] args) {
System.out.println(\"spring容器启动...\");
ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
System.out.println(\"关闭spring容器...\");
ioc.close();
System.out.println(\"关闭spring容器成功!\");
}
输出:
spring容器启动...
spring容器启动成功!
关闭spring容器...
关闭spring容器成功!
因为多例模式不是容器启动的时候创创建,而是在ioc.getBean(\"id\")时候创建该对象!
2.2当测试代码为:
public static void main(String[] args) {
System.out.println(\"spring容器启动...\");
ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
ioc.getBean(\"person\");------------------------>多例模式获取bean对象
System.out.println(\"关闭spring容器...\");
ioc.close();
System.out.println(\"关闭spring容器成功!\");
}
输出:
spring容器启动...
spring容器启动成功!
person的无参构造器方法...
person的初始化方法
关闭spring容器...
关闭spring容器成功!
结论:
1.当是单例模式时:Bean的生命周期
(容器启动)构造器方法---->初始化方法----->(容器关闭)销毁方法
2.多实例
获取bean(构造器------>初始化方法---->容器关闭(不会调用销毁方法))
9.Bean的后置处理器
1.自定义一个类实现BeanPostProcessor接口
public class MyBeanPostProcess implements BeanPostProcessor {
/**
* 自定义的初始化方法之前调用
* Object o是容器创建的bean
* String s是spring配置文件中配置的id
*/
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println(\"bean的后置处理器Befor...方法\");
System.out.println(s+\":\"+o);
return o;----->注意:这里不能return null,要不会报错
}
//自定义初始化方法之后执行
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println(\"bean的后置处理器After...方法\");
System.out.println(s+\":\"+o);
return o;------------------------->注意:这里如果return null;则ioc.getBean也是为null;
}
}
2.在spring配置文件中配置后置处理器
<!--实体类配置-->
<bean id=\"person01\" class=\"entity.Person\"
init-method=\"initMethod\"----------------------->perosn类的自定义初始化方法(person实例化时后会调用)
destroy-method=\"destroyMethod\">----------------->person类的自定义销毁方法(spring容器销毁前会调用)
<property name=\"age\" value=\"18\"></property>
<property name=\"name\" value=\"吴孟达\"></property>
</bean>
<!--后置处理器配置-->
<bean id=\"myBeanPostProcess\" class=\"Test.MyBeanPostProcess\"></bean>
3.测试代码如下:
public static void main(String[] args) {
System.out.println(\"加载spring....\");
ApplicationContext ioc=new ClassPathXmlApplicationContext(\"ioc.xml\");
System.out.println(\"spring容器启动成功!\");
Object bean= ioc.getBean(\"person01\");
System.out.println(\"容器获取的bean:\"+bean);
}
4.输出:
person执行无参构造器
person执行set age方法
person执行set name方法
bean的后置处理器Befor...方法
person01:Person{name=\'吴孟达\', age=18, book=null}
person自定义的初始化方法
bean的后置处理器After...方法
person01:Person{name=\'吴孟达\', age=18, book=null}
spring容器启动成功!
容器获取的bean:Person{name=\'吴孟达\', age=18, book=null}
结论:
发现带后置处理器的执行流程如下:
执行顺序:
- 1.bean实例化
- 2.执行bean的后置处理器的postProcessBeforeInitialization方法
- 3.执行自定义的初始化方法
- 4.执行bean后置处理器的postProcessAfterInitialization方法
来源:https://www.cnblogs.com/wmd-l/p/16226683.html
本站部分图文来源于网络,如有侵权请联系删除。