基于注解配置Spring
基于XML配置Spring
引入log4j日志系统
依赖反转,
装配bean的方法:
- 隐式的bean发现机制和自动装配
1. Spring框架两大核心机制
- IOC(控制反转)/DI(依赖注入)
- AOP(面向切面编程)
1.1. IOC(控制反转)
在Spring框架中,创建对象的流程交给IOC容器来创建,使用解耦合的方式
从IOC的bean工厂中获取对象:
通过ID获取
也可以通过运行时类获取(配置文件中只能存在该类的一个对象)
public void static main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MessagePrinter printer = context.getBean(MessagePrinter.class);//运行时类
//MessagePrinter printer = context.getBean("printer");//ID
printer.printMeaasge();
System.out.println(printer.toString());
}
1.1.1. 配置XML文件实现IOC的创建对象
配置文件详解:
- 通过配置bean标签来完成对bean类的管理
- id :表示对象名
- class:表示对象的对应类的全限定名(必须有无参构造函数)
- 对象的成员通过bean标签的子节点property来赋值(通过set()方法来赋值)
- name:成员变量名
- value:成员变量值(基本数据类型,String都可以直接赋值,其他引用类型不能通过value赋值)
- ref:将IOC中的另外一个bean赋给当前的成员变量(即DI)
<?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元素:描述当前的对象需要由spring容器管理 id属性:表示对象,可以根据id获取对象 class:被管理对象的名字包名+类名 service对象和成员变量的service对应 ref和service对应 --> <bean id="service" class="hello.MessageService"> <property name="age" value="25"></property> <property name="id" value="1"></property> <property name="name" value="张三"></property> </bean> <bean id="printer" class="hello.MessagePrinter"> <property name="service" ref="service"></property> <property name="name" value="惠普"></property> </bean> </beans>
- 通过constrctor-args标签调用有参构造来赋值
- 可以加name以及value,设值
- 也可以按照构造参数的顺序来写,也可以通过index根据变量有参构造的顺序设定传入参数的顺序
<?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="service" class="hello.MessageService"> <constructor-arg name="age" value="25"></constructor-arg> <constructor-arg name="id" value="1"></constructor-arg> <constructor-arg name="name" value="张三"></constructor-arg> </bean> <bean id="printer" class="hello.MessagePrinter"> <constructor-arg ref="service"></constructor-arg> <constructor-arg value="惠普"></constructor-arg> </bean> <!-- <bean id="printer" class="hello.MessagePrinter"> <constructor-arg index="0" ref="service"></constructor-arg> <constructor-arg index="1" value="惠普"></constructor-arg> </bean> --> </beans>
给bean类中注入集合
<bean id="student" class="hello.MessagePrinter"> <property name="age" value="33"></property> <property name="array"> <list> <ref bean="address"></ref> <ref bean="address2"></ref> </list> </property> </bean> </bean> <bean id="address2" class="hello.MessageService"> </bean>
1.1.2. IOC底层原理
- 读取配置文件解析XML
- 通过反射机制来实例化配置文件中配置的bean对象
package IOC;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class ClassPathXmlApplicationContext implements ApplicationContext {
private Map<String,Object> ioc = new HashMap<>();
public ClassPathXmlApplicationContext(String path){
try {
SAXReader reader = new SAXReader();
Document documented = reader.read("./src/main/resources/"+path);
Element root = documented.getRootElement();
Iterator<Element> iterator = root.elementIterator();
while (iterator.hasNext()){
Element element = iterator.next();
String id = element.attributeValue("id");
String classname = element.attributeValue("class");
Object object = Class.forName(classname).newInstance();//通过反射机制创建<bean>标签的对象
ioc.put(id,object);//id值为设定的类名,value为对象
Iterator<Element> beaniter = element.elementIterator();//通过<property>调用set方法给对象的属性赋值
while (beaniter.hasNext()){
Element property = beaniter.next();
String name = property.attributeValue("name");
String value = property.attributeValue("value");
// System.out.println(value);
String ref = property.attributeValue("ref");
if(ref==null){
String methodName = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
// System.out.println(methodName);
Field field = Class.forName(classname).getDeclaredField(name);
// System.out.println(field.getType());
Method method = Class.forName(classname).getMethod(methodName,field.getType());
// System.out.println(method);
//根据成员变量的数据类型将value进行转换,因为value为string类型的,
method.invoke(object,value);
}else{//在配置文件中DI的值必须定义在标签之上,如果在标签下会存在在map中找不到对象的情况
String methodName = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
Object di = ioc.get(ref);
Field field = Class.forName(classname).getDeclaredField(name);
Method method = Class.forName(classname).getMethod(methodName,field.getType());//获取对象
//根据成员变量的数据类型将value进行转换,因为value为string类型的,
method.invoke(object,di);
}
}
}
} catch (DocumentException | ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String id) {
return ioc.get(id);
}
}
1.1.3. 标签的Scope属性
Spring 管理的 bean 是根据 scope 来生成的,表示 bean 的作用域,共4种,默认值是 单例模式。
- singleton:单例,表示通过 IoC 容器的getBean方法获取的 bean 是唯一的。
- prototype:原型,表示通过 IoC 容器的getBean方法获取的 bean 是不同的。
- request:请求,表示在一次 HTTP 请求内有效。
- session:回话,表示在一个用户会话内有效。
request 和 session 只适用于 Web 项目,大多数情况下,使用单例和原型较多。
prototype 模式当业务代码获取 IoC 容器中的 bean 时,即调用getBean()方法时,Spring 才去调用无参构造创建对应的 bean。
singleton 模式无论业务代码是否获取 IoC 容器中的 bean,Spring 在加载 spring.xml 时就会创建 bean.
1.1.4. 标签中bean类的继承
对于一个子对象来说,可以继承父对象的属性值,可以是不同类之间的继承。Spring 的继承关注点在于具体的对象,而不在于类,即不同的两个类的实例化对象可以完成继承,前提是子对象必须包含父对象的所有属性,同时可以在此基础上添加其他的属性。
<bean id="student" class="com.Student">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="22"></property>
</property>
</bean>
<bean id="stu" class="com.Stu" parent="student">
<property name="course" value="数学"></property>
</bean>
public class Student{
private Integer id;
private String name;
private Integer age;
//set方法等
}
public class Stu{
private Integer id;
private String name;
private Integer age;
private String course;
}
1.1.5. bean类的依赖
与继承类似,依赖也是描述 bean 和 bean 之间的一种关系,配置依赖之后,被依赖的 bean 一定先创建,再创建依赖的 bean,A 依赖于 B,先创建 B,再创建 A。
<?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-3.2.xsd
">
<bean id="student" class="com.southwind.entity.Student" depends-on="user"></bean>
<bean id="user" class="com.southwind.entity.User"></bean>
</beans>
1.1.6. Spring的p命名空间
p 命名空间是对 IoC / DI 的简化操作,使用 p 命名空间可以更加方便的完成 bean 的配置以及 bean 之间的依赖注入,使用p简化书写,加xxx-ref代表为其他对象
<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<bean id="student" class="com.southwind.entity.Student" p:id="1" p:name="张三" p:age="22" p:address-ref="address"></bean>
<bean id="address" class="com.southwind.entity.Address" p:id="2" p:name="科技路"></bean>
</beans>
1.1.7. Spring的工厂方法获取bean对象
IoC 通过工厂模式创建 bean 的方式有两种:
- 静态工厂方法
- 实例工厂方法
<?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="service" class="factory.springFactory" factory-method="get">
<constructor-arg value="1"></constructor-arg>
</bean>
</beans>
package factory;
import hello.MessageService;
import java.util.HashMap;
import java.util.Map;
public class springFactory {
private static Map<Integer,MessageService> all = new HashMap<>();
static {
all.put(1,new MessageService("1","tom","55"));
all.put(2,new MessageService("1","tom","55"));
}
public static MessageService get(Integer i){
return all.get(i);
}
}
package factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLOutput;
public class test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-Factory.xml");
System.out.println(context.getBean("service"));
}
}
实例工厂方法:
<?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="service" class="factory.springFactory" factory-method="get">
<constructor-arg value="1"></constructor-arg>
</bean>
<!--实例工厂-类->
<bean id="carFactory" class="factory.InstanceFactory"></bean>
<!--获取变量-->
<bean id="s2" factory-bean="carFactory" factory-method="get">
<constructor-arg value="1"></constructor-arg>
</bean>
</beans>
package factory;
import hello.MessageService;
import java.util.HashMap;
import java.util.Map;
public class InstanceFactory {
private Map<Integer, MessageService> alll;
public InstanceFactory(){
alll = new HashMap<>();
alll.put(1,new MessageService("1","tom","55"));
alll.put(2,new MessageService("1","tom","55"));
}
public MessageService get(Integer i){
return alll.get(i);
}
}
package factory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLOutput;
public class test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-Factory.xml");
System.out.println(context.getBean("s2"));
}
}
1.1.8. IoC 自动装载(Autowire)
IoC 负责创建对象,DI 负责完成对象的依赖注入,通过配置 property 标签的 ref 属性来完成,同时 Spring 提供了另外一种更加简便的依赖注入方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成注入。
自动装载有两种方式:
- byName:通过成员变量的属性名自动装载,即创建对象的名字与成员变量名相同
- byType:通过属性的数据类型自动装载,但是只能有一个这个类的实例
<bean id="service" class="hello.MessageService">
<property name="name" value="tosssss"></property>
</bean>
<bean id="printer" class="hello.MessagePrinter" autowire="byName"><!--自动装载-->
<property name="name" value="惠普"></property>
<!-- <property name="service" ref="service"></property>-->
</bean>
<bean id="service" class="hello.MessageService">
<property name="name" value="tosssss"></property>
</bean>
<bean id="printer" class="hello.MessagePrinter" autowire="byType"><!--自动装载-->
<property name="name" value="惠普"></property>
<!-- <property name="service" ref="service"></property>-->
</bean>
byType 需要注意,如果同时存在两个及以上的符合条件的 bean 时,自动装载会抛出异常。
1.2. AOP(面向切面编程)
Spring框架对动态代理进行封装。
Spring 框架中不需要创建 InvocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可,Spring 框架底层会自动根据切面类以及目标类生成一个代理对象。
使用动态代理实现
在Spring中实现
//代理类
package com.southwind.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect//表示该类为切面类
@Component//给IOC来管理对象
public class LoggerAspect {
@Before(value = "execution(public int com.southwind.utils.impl.CalImpl.*(..))")//表示位置,参数为类
public void before(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
//获取参数
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name+"方法的参数是:"+ args);
}
@After(value = "execution(public int com.southwind.utils.impl.CalImpl.*(..))")
public void after(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法执行完毕");
}
@AfterReturning(value = "execution(public int com.southwind.utils.impl.CalImpl.*(..))",returning = "result")//returning以及参数Object必须相同
public void afterReturning(JoinPoint joinPoint,Object result){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法的结果是"+result);
}
@AfterThrowing(value = "execution(public int com.southwind.utils.impl.CalImpl.*(..))",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception){
//获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法抛出异常:"+exception);
}
}
LoggerAspect 类定义处添加的两个注解:
- @Aspect:表示该类是切面类。
- @Component:将该类的对象注入到 IoC 容器。
具体方法处添加的注解:
@Before
委托对象的类 也需要添加 Component,交给 IoC 容器来管理。
package com.southwind.utils.impl;
import com.southwind.utils.Cal;
import org.springframework.stereotype.Component;
@Component
public class CalImpl implements Cal {
public int add(int num1, int num2) {
int result = num1+num2;
return result;
}
public int sub(int num1, int num2) {
int result = num1-num2;
return result;
}
public int mul(int num1, int num2) {
int result = num1*num2;
return result;
}
public int div(int num1, int num2) {
int result = num1/num2;
return result;
}
}
spring.xml 中配置 AOP。
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!-- 自动扫描 -->
<context:component-scan base-package="com.hello"></context:component-scan>
<!-- 是Aspect注解生效,为目标类自动生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import utills.Cal;
import java.lang.reflect.Proxy;
public class Test {
ApplicationContext context = new ClassPathXmlApplicationContext("AOP.xml");
Cal proxy = (Cal)context.getBean("calImpl");//默认ID为委托类的名字首字母小写,可以通过委托类compoent(value="")配置
proxy.add(1,1);
}
context:component-scan 将 hello包中的所有类进行扫描,如果该类同时添加了 `@Component,则将该类扫描到 IoC 容器中,即 IoC 管理它的对象。
aop:aspectj-autoproxy 让 Spring 框架结合切面类和目标类自动生成动态代理对象。
- 切面:横切关注点被模块化的抽象对象。
- 通知:切面对象完成的工作。
- 目标:被通知的对象,即被横切的对象。
- 代理:切面、通知、目标混合之后的对象。
- 连接点:通知要插入业务代码的具体位置。
- 切点:AOP 通过切点定位到连接点。