spring一
BeanPostProcessor
bean
:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Bean {
private static final Logger log = LoggerFactory.getLogger(Bean.class);
public Bean() {
log.info("construct bean");
}
}
beanPostProcessor
:
public class MyBeanPostProcessor2 implements BeanPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MyBeanPostProcessor2.class);
public MyBeanPostProcessor2() {
log.info("construct mybeanpostProcessor");
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof Bean) {
log.info("process bean before initialization");
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof Bean) {
log.info("process bean after initialization");
}
return bean;
}
}
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">
<!--当我们加载spring框架时,spring就会自动创建一个bean对象,并放入内存相当于我们常规的new一个对象,而<property></property>中的value则是实现了“对象.set方法”,这里也体现了注入了概念-->
<bean id="myBean" class="cn.orzlinux.Bean.BeanPostProcessorTest.Bean"></bean>
<bean id="myBeanPostProcessor2" class="cn.orzlinux.Bean.BeanPostProcessorTest.MyBeanPostProcessor2"></bean>
</beans>
测试代码(普通测试,非test):
public class Test {
public static void main(String[] args) throws InterruptedException {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean my = (Bean) ac.getBean("myBean");
System.out.println(my);
}
}
运行结果:
[main] INFO MyBeanPostProcessor2 - construct mybeanpostProcessor
[main] INFO Bean - construct bean
[main] INFO MyBeanPostProcessor2 - process bean before initialization
[main] INFO MyBeanPostProcessor2 - process bean after initialization
显示:是先创建BeanPostProcessor
对象,然后创建bean
对象,再执行BeanPostProcessor
的before
和after
方法。可以利用它,在对象创建之后,对对象进行修改。
查看调用栈:
进入看源码:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// here ======================================
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
// 其实这个方法就是Spring提供的,用于对象创建完之后,针对对象的一些初始化操作。
// 这就好比你创建了一个英雄之后,你需要给他进行一些能力属性的初始化、服装初始化一样。
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
// here =====================================
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
先后调用了before
和after
方法。以before
为例:
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
Object current;
for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
BeanPostProcessor processor = (BeanPostProcessor)var4.next();
current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
}
return result;
}
这两个方法内部,则分别去遍历系统里所有的BPP,然后逐个执行这些BPP对象before
和after
方法,去处理对象。
bean初始化图:
Spring核心知识
本节转载自:Spring核心知识详细教程(已完结),作者EasyChill,点击跳转。
声明:
本文全文手写,代码全部手写,也希望大家,可以做一遍,最起码调试一遍,这样比看的效果好的多,本文的spring使用的是5.0.4版本,ide使用的是IntelliJ IDEA,不足和错误之处还请大家指出,谢谢!!
一、spring是什么
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
二、spring快速入门
什么是spring?
首先我们了解到struts是web框架(jsp/action/actionform)
hibernate是 orm框架处于持久层
spring是容器框架,用于配置bean,并维护bean之间的关系的框架
- spring中的bean:是一个很重要的概念,这里的bean可以是Java中的任何一种对象:JavaBean/service/action/数据源/dao等等
spring中的ioc(inverse of control 控制反转)
spring中的di(dependency injection 依赖注入)
接下来看一个层次框架图:
说明:
web层: struts充当web层,接管jsp,action,表单,主要体现出mvc的数据输入,数据的处理,数据的显示分离
model层: model层在概念上可以理解为包含了业务层,dao层,持久层,需要注意的是,一个项目中,不一定每一个层次都有
持久层:体现oop,主要解决关系模型和对象模型之间的阻抗
入门项目:
创建java项目(web中也可以使用)
创建lib文件夹引入spring的开发最小包(最小配置,spring.jar(该包把最常用的包都包括),commons-logging.jar(日志包))
创建配置文件,一般在src目录下
配置bean
说明:
<bean></bean>
这对标签元素的作用:当我们加载spring框架时,spring就会自动创建一个bean对象,并放入内存相当于我们常规的new一个对象,而<property></property>
中的value则是实现了“对象.set方法”,这里也体现了注入了概念然后在java文件(测试文件)中调用
接下来看具体的项目:
说明:这是我的目录结构,其中我使用了ide整合了jar包,如果是手动创建时只需将jar包导入到项目里即可
User.java
package com.nuc.Bean; public class User { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String sayHello(){ System.out.println("hello"+ name); return "true"; } }
抛开spring框架,使用传统方式实现在测试类中调用sayHello方法:
这样,没有异议吧。
接下来使用spring调用该方法
结果为小强,是因为上面的配置文件中配置value为小强
这样一个基本的项目就完成了~
接下来是细节:
- 创建User2这个类
package com.nuc.Bean; public class User2 { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void sayBye(){ System.out.println("bye"+name); } }
- 在User中新增
private User2 user2;
并在sayHello中调用sayBye方法 - 执行test类报出错误,这是由于user2未注入
- 在配置文件中配置注入
- 再次运行测试类 spring运行原理图 入门项目小结:
spring实际上是容器框架,可以配置各种bean,并可以维护bean与bean的关系,当我们需要使用某个bean的时候,我们可以直接getBean(id),使用即可
现在我们来回答什么是spring这个问题
spring是一个容器框架,它可以接管web层,业务层,dao层,持久层的各个组件,并且可以配置各种bean, 并可以维护bean与bean的关系,当我们需要使用某个bean的时候,我们可以直接getBean(id),使用即可
接下来对几个重要的概念做说明:
- ioc是什么?
- ioc(inverse of control)控制反转:所谓反转就是把创建对象(bean)和维护对象(bean)的关系的权利从程序转移到spring的容器(spring-config.xml)
- di是什么?
- di(dependency injection)依赖注入:实际上di和ioc是同一个概念,spring的设计者,认为di更准确的表示spring的核心
实质上学习框架就是,最重要的就是学习各个配置
三、接口编程
spring就提倡接口编程,在配合di技术就可以达到层与层解耦的目的
举案例说明:
这个项目实现的是大小写转换
基本思路:
- 创建一个接口
- 创建两个类实现接口
- 配置bean
- 使用
下面是我的项目目录
ChangeLetter.java
package com.nuc;
public interface ChangeLetter {
public String change();
}
LowerLetter.java
package com.nuc;
public class LowerLetter implements ChangeLetter {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String change(){
//大小字母转小写
return str.toLowerCase();
}
}
UpperLetter.java
package com.nuc;
public class UpperLetter implements ChangeLetter {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public String change(){
//把小写字母转成大写
return str.toUpperCase();
}
}
spring-config.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">
<!--<bean id="changeLetter" class="com.nuc.UpperLetter">-->
<!--<property name="str">-->
<!--<value>sjt</value>-->
<!--</property>-->
<!--</bean>-->
<bean id="changeLetter" class="com.nuc.LowerLetter">
<property name="str" value="SJT"/>
</bean>
</beans>
说明:其中的两个bean id名相同是为了调试方便,可通过注释来调试
Test.java
package com.nuc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
//调用change不使用接口
// UpperLetter changeLetter = (UpperLetter) ac.getBean("changeLetter");
// System.out.println(changeLetter.change());
//使用接口
ChangeLetter changeLetter = (ChangeLetter)ac.getBean("changeLetter");
System.out.println(changeLetter.change());
}
}
以上这个案例,我们可以初步体会到,di和接口编程,的确可以减少层(web层)和层(业务层)之间的耦合度,尽管看起来似乎没什么改变,而且好像麻烦了一些,但是当项目大了以后,这种耦合度的降低就显得尤为重要
四、获取Bean
- ApplicationContext 应用上下文容器取
当这句代码被执行,spring-config.xml文件中配置的bean就会被实例化。(但要注意bean的生命周期要为singleton),也就是说,不管没有getBean(),使用上下文容器获取bean,就会实例化该beanApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
- Bean工厂容器取
这句代码被执行,spring-config.xml文件中配置的bean不会被实例化,即光实例化容器,并不会实例化bean 而是在执行以下代码时才会被实例化,即使用bean的时候:BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
factory.getBean("beanId");
如何验证上述说法呢?每一个java类都有一个默认的构造方法。给这个构造方法输出一句话。具体如下:- 创建一个类,类有一个属性,装配,该属性
- 重写该类的构造方法,输出bean被创建
- 创建测试类,测试
- 使用ApplicationContext应用上下文容器*
这样就是bean实例化了 那么在实际开发中选择哪种方式? 在移动开发中,即项目运行在移动设备中使用BeanFactory(节约内存,所以,你想节约内存开发就是使用bean工厂,但速度会受影响),但大部分的项目都是使用ApplicationContext(可以提前加载,缺点是消耗一点内存) 贴一张bean的生命周期图: 接下来我们验证前两种作用域: 第一种 结果 可以看到stu1和stu2拥有相同的地址,接下来测试第二种 测试结束! 至于后三种是在web开发中才有实际意义!可以看到,这一行代码,并不能时bean实例化,接下来加factory.getBean(“student”);试试
五、三种获取ApplicationContext对象引用的方法
ClassPathXmlApplicationContext (从类路径中加载)
- 这个不在赘述,上面所有例子都是利用这种方式加载的
FileSystemXmlApplicationContext (从文件系统中加载)
可以看到是没有问题的,需要注意的是,文件路径为绝对路径,且注意使用转义符,直接使用“C:\sjt\idea\code\spring\spring-interface\src”,会报错,需要将“\”转义,但实际开发中应用不多,了解即可
XmlWebApplicationContext (从web系统中加载)
这种方式,注意,在tomcat启动时就会加载,此处不做说明,在web应用中说明
六、再谈Bean的生命周期
- 生命周期是一个重点吗?答案是肯定的!!
- 不了解生命周期难道不能开发了吗?那自然是可以的,但如果你想实现更加高级的功能,你不了解那可能是会出问题的!而在面试过程中也是经常提及的。
- 接下里我们举例子说明
- 生命周期分为以下几步:
- 1、实例化
- 当我们加载sping-config.xml文件时,bean就会被实例化到内存(前提是scope=singleton)
- 2、设置属性值
- 调用set方法设置属性,前提是有对应的set方法
- 3、如果你调用BeanNameAware的set’Bean’Name()方法
- 这是个接口,该方法可以给出正在被调用的bean的id
- 4、如果你调用BeanFactoryAware的setBeanFactory()方法
- 这也是个接口,该方法可以传递beanFactory
- 5、如果你调用了ApplicationContextAeare的setApplicationContext()方法
- 同样为接口,该方法传递一个ApplicationContext
- 6、BeanPostProcessor的预初始化方法Before
- 这个东西很厉害了,可以叫做后置处理器,它不是接口,具体细节,代码体现
- 7、如果你调用了InitializingBean的afterPropertiesSet()方法
- 8、调用自己的init方法,具体为在bean中有一个属性inin-method=”init”
- 9、BeanPostProcessor的方法After
- 10、使用bean,体现为调用了sayHi()方法
- 11、容器关闭
- 12、可以实现DisposableBean接口的destory方法
- 13、可以在调用自己的销毁方法,类似于8
- 实际开发过程中,并没有这么复杂,常见过程为,1,2,6,9,10,11*
- 接下来看代码*
- 1、实例化
- 生命周期分为以下几步:
package com.nuc.BeanLife; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("第九步,postProcessAfterInitialization方法被调用"); return null; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("第六步,postProcessBeforeInitialization方法被调用"); System.out.println("第六步,"+bean+"被创建的时间为"+new java.util.Date()); /* 在这里,能做的事情可就不止上面的这么简单的一句输出了,它还可以过滤每个对象的ip 还可以给所有对象添加属性或者函数,总之就是所有对象! 其实,这里体现了AOP编程的思想,AOP呢就是面向切成编程(针对所有对象编程) */ return null; } }
PersonService.java
package com.nuc.BeanLife;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class PersonService implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("第二步调用set方法");
}
public void sayHi(){
System.out.println("第十步,hi"+ name);
}
public PersonService(){
System.out.println("第一步,实例化bean");
}
@Override
public void setBeanName(String arg0){
//该方法可以给出正在被调用的bean的id
System.out.println("第三步,setBeanName被调用,调用的id名为:"+arg0);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//该方法可以传递beanFactory
System.out.println("第四步,setBeanFactory被调用,beanFactory为:"+beanFactory);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//该方法传递一个ApplicationContext
System.out.println("第五步,调用setApplicationContext方法:"+applicationContext);
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第七步,调用afterPropertiesSet()方法");
}
public void init(){
System.out.println("第八步、调用我自己的init()方法");
}
@Override
public void destroy() throws Exception {
//关闭数据连接,socket,文件流,释放资源
//这个函数的打印你看不到,应为
System.out.println("第十步,销毁方法(但不建议使用这种方式释放资源)");
}
public void destory(){
// 也看到不
System.out.println("销毁");
}
}
Test.java
package com.nuc.BeanLife;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
PersonService person1= (PersonService) ac.getBean("personService");
person1.sayHi();
}
}
spring-config.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">
<bean id="personService" init-method="init" destroy-method="destroy" scope="singleton" class="com.nuc.BeanLife.PersonService">
<property name="name" value="sjt"></property>
</bean>
<bean id="personService2" class="com.nuc.BeanLife.PersonService">
<property name="name" value="sjt2"></property>
</bean>
<!--配置自己的后置处理器,优点类似filter-->
<bean id="myBeanPostProcessor" class="com.nuc.BeanLife.MyBeanPostProcessor">
</bean>
</beans>
测试结果
4月 17, 2018 4:57:26 下午
信息: Loading XML bean definitions from class path resource [spring-config.xml]
第一步,实例化bean
第二步调用set方法
第三步,setBeanName被调用,调用的id名为:personService
第四步,setBeanFactory被调用,beanFactory为:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,调用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被调用
第六步,com.nuc.BeanLife.PersonService@2e6a8155被创建的时间为Tue Apr 17 16:57:27 CST 2018
第七步,调用afterPropertiesSet()方法
第八步、调用我自己的init()方法
第九步,postProcessAfterInitialization方法被调用
第一步,实例化bean
第二步调用set方法
第三步,setBeanName被调用,调用的id名为:personService2
第四步,setBeanFactory被调用,beanFactory为:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,调用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被调用
第六步,com.nuc.BeanLife.PersonService@6221a451被创建的时间为Tue Apr 17 16:57:27 CST 2018
第七步,调用afterPropertiesSet()方法
第九步,postProcessAfterInitialization方法被调用
第十步,hisjt
Process finished with exit code 0
动手做一遍是最好的选择!!
使用bean工厂获取bean对象,生命周期是和上下文获取的不一样的,如下图
其中我只装配了一个bean,可见执行步骤的短缺
七、装配Bean
使用xml装配
上下文定义文件的根元素是
<beans></beans>
,有多个子元素<bean></bean>
,每个<bean>
元素定义了bean如何被装配到spring容器中对子元素bean最基本的配置包括bean的ID和它的全称类名
对bean的scope装配,默认情况下为单例模式,具体情况上面已经说过,建议查看文档,更加具体,尽量不要使用原型bean,即scope设置为propotype,这样子会对性能有较大的影响
bean的init-metho和destory-method的书写,在生命周期那一块儿已经很清楚了,此处不再赘述,需要说明的是,可以通过注解的方式来配置,而不是在bean中使用init-metho和destory-method属性
注入集合类型的数据,例如,map,set,list,数组,Properties….
接下来举例子
目录结构:
Department.java
package com.nuc; import java.util.List; import java.util.Map; import java.util.Set; public class Department { private String name; private String []empName;//这里int的数组也可以注入成功 private List<Employee> empList; private Map<String,Employee> empMap; private Properties pp; public Properties getPp() { return pp; } public void setPp(Properties pp) { this.pp = pp; } public Set<Employee> getEmpSet() { return empSet; } public void setEmpSet(Set<Employee> empSet) { this.empSet = empSet; } private Set<Employee> empSet; public List<Employee> getEmpList() { return empList; } public void setEmpList(List<Employee> empList) { this.empList = empList; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String[] getEmpName() { return empName; } public void setEmpName(String[] empName) { this.empName = empName; } public Map<String, Employee> getEmpMap() { return empMap; } public void setEmpMap(Map<String, Employee> empMap) { this.empMap = empMap; } }
Employee.java
package com.nuc;
public class Employee {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test.java
package com.nuc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
Department department = (Department)ac.getBean("department");
System.out.println(department.getName());
// 取集合
for(String empName:department.getEmpName()){
System.out.println(empName);
}
System.out.println("取list...");
for (Employee e:department.getEmpList()){
System.out.println("name="+e.getName());
}
System.out.println("取set...");
for (Employee e:department.getEmpSet()){
System.out.println("name="+e.getName());
}
System.out.println("迭代器取map...");
//1.迭代器
Map<String,Employee> employeeMap = department.getEmpMap();
Iterator iterator = employeeMap.keySet().iterator();
while (iterator.hasNext()){
String key = (String)iterator.next();
Employee employee=employeeMap.get(key);
System.out.println("key="+key+" "+ employee.getName());
}
System.out.println("entry取map...");
//2.简洁(建议使用这种方式)
for (Entry<String,Employee> entry:department.getEmpMap().entrySet()){
System.out.println(entry.getKey()+" "+entry.getValue().getName());
}
}
System.out.println("通过properties取数据");
Properties properties = department.getPp();
for (Entry<Object,Object> entry:properties.entrySet()){
System.out.println(entry.getKey().toString()+" "+entry.getValue());
}
}
spring-config.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">
<bean id="department" class="com.nuc.Department">
<property name="name" value="财务部"></property>
<!--给数组注入-->
<property name="empName">
<list>
<value>小明</value>
<value>小花</value>
</list>
</property>
<!--给list注入-->
<!--list可以存放相同的对象,并当作不同对象输出-->
<property name="empList">
<list>
<ref bean="employee1"></ref>
<ref bean="employee2"></ref>
</list>
</property>
<!--给set注入-->
<!--set集合不可以存放相同对象-->
<property name="empSet">
<set>
<ref bean="employee1"></ref>
<ref bean="employee2"></ref>
</set>
</property>
<!--给map注入-->
<!--输出的对象取决于key值,key值不同,对象相同也可以打出-->
<!--当key值相同时,对象相同或者不同都打出最后一个key所对应的对象-->
<property name="empMap">
<map>
<entry key="1" value-ref="employee1"></entry>
<entry key="2" value-ref="employee2"></entry>
<entry key="3" value-ref="employee2"></entry>
</map>
</property>
<!--给属性集合注入-->
<property name="pp">
<props>
<prop key="1">hello</prop>
<prop key="2">world</prop>
</props>
</property>
</bean>
<bean id="employee1" class="com.nuc.Employee">
<property name="name" value="北京"></property>
</bean>
<bean id="employee2" class="com.nuc.Employee">
<property name="name" value="太原"></property>
</bean>
</beans>
Student.java
package com.nuc.inherit;
public class Student {
protected String name;
protected int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Gradate.java
package com.nuc.inherit;
public class Gradate extends Student {
private String degree;
public String getDegree() {
return degree;
}
public void setDegree(String degree) {
this.degree = degree;
}
}
spring-config.java
<?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">
<!--配置一个学生对象-->
<bean id="student" class="com.nuc.inherit.Student">
<property name="name" value="sjt"></property>
<property name="age" value="22"></property>
</bean>
<!--配置gradate对象-->
<bean id="gradate" parent="student" class="com.nuc.inherit.Gradate">
<!--如果子类重新赋值,则覆盖父类的-->
<property name="name" value="小明"></property>
<property name="degree" value="博士"></property>
</bean>
</beans>
Test2.java
package com.nuc.inherit;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/inherit/spring-config.xml");
Gradate gradate = (Gradate) ac.getBean("gradate");
System.out.println(gradate.getName()+" "+gradate.getAge()+" "+gradate.getDegree());
}
}
以上我们都是用set注入依赖的,下面介绍构造函数注入依赖
<bean name="user" class="com.nuc.Bean.User"> <!--通过constructor-arg标签完成了对构造方法的传参--> <!--如果是属性是类类型,则使用ref=""--> <constructor-arg index="0" type="java.lang.String" value="小强"></constructor-arg> <constructor-arg index="1" type="java.lang.String" value="男"></constructor-arg> <constructor-arg index="2" type="int" value="20"></constructor-arg> </bean>
当然对应的User要有相应的构造方法。
set注入的缺点是无法清晰的表达哪个属性是必须的,哪些是可选的,构造器注入的优势,是可以通过构造强制依赖关系,不可能实例化不完全或者不能使用的bean
但其实实际开发中还是set注入较多,即property注入
bean的自动装配:
接下来是实例:
目录图
Dog.java
package com.nuc.autowire;
public class Dog {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Master.java
package com.nuc.autowire;
public class Master {
private String name;
private Dog dog;
private Master(Dog dog){
//为了自动装配的constructor
this.dog= dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
Test.java
package com.nuc.autowire;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com\\nuc\\autowire\\beans.xml");
Master master = (Master)ac.getBean("master");
System.out.println(master.getName()+"养了只狗,它的名字叫"+ master.getDog().getName()+",他今年"+master.getDog().getAge()+"岁了");
}
}
beans.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">
<!--配置master对象-->
<bean id="master" class="com.nuc.autowire.Master" autowire="constructor">
<property name="name" value="sjt"></property>
<!--传统方式-->
<!--<property name="dog" ref="dog"></property>-->
</bean>
<!--配置dog对象,byName时使用-->
<!--<bean id="dog" class="com.nuc.autowire.Dog">-->
<!--<property name="name" value="小黄"></property>-->
<!--<property name="age" value="2"></property>-->
<!--</bean>-->
<!--配置dog对象,byType时使用-->
<!--<bean id="dog11" class="com.nuc.autowire.Dog">-->
<!--<property name="name" value="小黄"></property>-->
<!--<property name="age" value="2"></property>-->
<!--</bean>-->
<!--配置dog对象,constructor时使用-->
<bean id="dog22" class="com.nuc.autowire.Dog">
<property name="name" value="小黄"></property>
<property name="age" value="2"></property>
</bean>
</beans>
autodetect:是在constructor和byType之间选一种
default:这种方式在文档中没有提及,需要在beans中指定,当你在beans中指定以后,所有的bean都是你所指定的装配方式,如果没有指定,则默认为no,所以,no之所以为默认指定装配方式,其实是从beans那里来的
其实在实际开发中,很少用到自动装配, 一般都是手动set装配的(property),而且自动装配也是在bean中没有配置才取执行自动装配的
- spring本身提供的bean
- 分散配置
- spring本身提供的bean
八、AOP编程(难点)
- aop:aspect oriented programming(面向切面编程),它是对一类对象或所有对象编程。
- 核心:在不增加代码的基础上,还增加新功能
- 提醒:aop编程,实际上是开发框架本身用的多,开发中不是很多,将来会很多
- 初步理解:面向切面:其实是,把一些公共的“东西”拿出来,比如说,事务,安全,日志,这些方面,如果你用的到,你就引入。
- 定义接口
- 编写对象(被代理对象=目标对象)
- 编写通知(前置通知目标方法调用前调用)
- 在beans.xml中配置
- 配置被代理对象
- 配置通知
- 配置代理对象(是proxyFactoryBean的对象实例)
- 配置代理接口集
- 织入通知
- 配置被代理对象
TestServiceInter.java(interface)
package com.nuc.Aop;
public interface TestServiceInter {
public void sayHello();
}
TestServiceInter2.java(interface)
package com.nuc.Aop;
public interface TestServiceInter2 {
public void sayBye();
}
TestService.java
package com.nuc.Aop;
public class TestService implements TestServiceInter,TestServiceInter2{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void sayHello(){
System.out.println("hi "+name);
}
@Override
public void sayBye() {
System.out.println("bye "+name);
}
}
MyMethodBeforeAdvice.java
package com.nuc.Aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
//前置通知
@Override
public void before(Method method, Object[] objects, Object o)
throws Throwable {
//method:被调用方法的名字
//objects:给method传递的参数
//o:目标对象
System.out.println("记录日志。。。"+method.getName());
}
}
beans.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">
<!--配置被代理的对象-->
<bean id="testService" class="com.nuc.Aop.TestService">
<property name="name" value="sjt"/>
</bean>
<!--配置前置通知-->
<bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
<!--配置代理对象-->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置代理接口-->
<property name="proxyInterfaces">
<list>
<value>com.nuc.Aop.TestServiceInter</value>
<value>com.nuc.Aop.TestServiceInter2</value>
</list>
</property>
<!--把通知织入到代理对象-->
<property name="interceptorNames">
<!--相当于把myMethodBeforeAdvice前置通知和代理对象关联起来-->
<value>myMethodBeforeAdvice</value>
</property>
<!--配置被代理对象,可以指定-->
<property name="target" ref="testService"></property>
</bean>
</beans>
Test.java
package com.nuc.Aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/Aop/beans.xml");
TestServiceInter testService = (TestServiceInter)ac.getBean("proxyFactoryBean");
testService.sayHello();
//当一个类继承多个接口,那么他们之间可以互转
((TestServiceInter2)testService).sayBye();
}
}
测试结果
- AOP的术语
- 切面:要实现交叉功能,是系统模块化的一个切面领域,如记录日志
- 连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段
- 连接点是一个静态的概念
- 通知: 切面的实际实现,它通知系统的新行为,如日志通知包含了实现日志功能的代码,如向日志文件写日志,通知在连接点插入应用系统中。
- 切入点:定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点
- 切入点是动态概念,当通知应用了连接点,连接点就变成了切入点
- 引入:为类添加新方法和属性
- 目标对象:被通知的对象,既可以是你编写的类,也可以是第三方类
- 代理:将通知应用到目标对象后创建后的对象,应用系统的其他部分不用为了支持代理对象而改变
- spring的两种代理:
- 若目标对象实现了若干个接口,spring使用JDK的java.lang.reflect.Proxy类代理
- 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类
- spring的两种代理:
- 织入:将切面应用到目标对象从而创建一个新代理对象的过程,织入发生在目标对象生命周期的多个点上
- 编译期:切面在目标对象编译时织入,这需要一个特使的编译器
- 类装载期:切面在目标对象被载入jvm时织入,这需要一个特殊的类加载器
- 运行期:切面在应用系统运行时切入
MyAfterReturningAdvice.java
package com.nuc.Aop;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects,
Object o1)
throws Throwable {
//后置通知
//o:前面函数的返回值
//method:哪个方法被调用
//objects:调用方法的参数
//o1:目标对象
System.out.println("后置通知:调用结束,关闭资源。");
}
}
MyMethodInterceptor.java
package com.nuc.Aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//环绕通知
System.out.println("环绕通知:进入函数体,调用方法前");
Object obj = methodInvocation.proceed();
System.out.println("环绕通知:完成调用");
return obj;
}
}
MyThrowsAdvice.java
package com.nuc.Aop;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class MyThrowsAdvice implements ThrowsAdvice {
//异常通知
//ThrowsAdvice这个接口是标识性接口,没有任何方法
public void afterThrowing(Method m,Object[] os,Object target,Exception e){
System.out.println("异常通知:出问题了:"+e.getMessage());
}
}
beans.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">
<!--配置被代理的对象-->
<bean id="testService" class="com.nuc.Aop.TestService">
<property name="name" value="sjt"/>
</bean>
<!--配置前置通知-->
<bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
<!--配置后置通知-->
<bean id="myAfterReturningAdvice" class="com.nuc.Aop.MyAfterReturningAdvice"></bean>
<!--配置环绕通知-->
<bean id="myMethodInterceptor" class="com.nuc.Aop.MyMethodInterceptor"></bean>
<!--配置异常通知-->
<bean id="myThrowsAdvice" class="com.nuc.Aop.MyThrowsAdvice"></bean>
<!--定义前置通知的切入点(引用通知)-->
<bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice"></property>
<property name="mappedNames">
<list>
<!--这里支持使用正则表达式匹配-->
<!--配置了sayHello使用前置通知过滤-->
<value>sayHello</value>
</list>
</property>
</bean>
<!--配置代理对象-->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置代理接口-->
<property name="proxyInterfaces">
<list>
<value>com.nuc.Aop.TestServiceInter</value>
<value>com.nuc.Aop.TestServiceInter2</value>
</list>
</property>
<!--把通知织入到代理对象-->
<property name="interceptorNames">
<list>
<!--相当于把myMethodBeforeAdvice前置通知和代理对象关联起来-->
<!--使用自定义切入点-->
<value>myMethodBeforeAdviceFilter</value>
<!--织入后置通知-->
<value>myAfterReturningAdvice</value>
<!--织入环绕通知-->
<value>myMethodInterceptor</value>
<!--织入异常通知-->
<value>myThrowsAdvice</value>
</list>
</property>
<!--配置被代理对象,可以指定-->
<property name="target" ref="testService"></property>
</bean>
</beans>
TestService.java
如图这一处变动
总之呢就是一个配置 -> 织入的过程
运行结果:
可以看到前置通知和后置通知,似乎能够识别方法,事实上也是这样的(spring框架内置)。而且sayBay()也得到了应用。这正是,我们前面所提到了,AOP是对一类或所有对象编程的体现,又由于异常通知的配置,有了异常,由于引用通知的配置,致使sayBay的前置通知及后续无法通知。
正常结果(配置引用通知):
PS
报错:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
解决方案:
我原来用的是api,不是simple。
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>