IoC是什么?
ioc通过文件配置来实现对象的创建,以及对象的赋值和注入。
IoC的依赖
ioc所依赖的包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.wiggin</groupId> <artifactId>aispring</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <repositories> <repository> <id>huaweicloud</id> <name>huaweicloud</name> <url>>https://mirrors.huaweicloud.com/repository/maven/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.7.RELEASE</version> </dependency> <!-- 简化实体类代码开发--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> <scope>provided</scope> </dependency>
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.1</version> </dependency> </dependencies>
</project> |
@Data:控制数据输出成String类型,以及getter,setter函数
@AllArgsConstructor:快速条件构造函数
@NoArgsConstructor:添加无参构造
1 2 3 4 5 6 7 8 9 10 | package Com.wiggin.entity; @Data @AllArgsConstructor @NoArgsConstructor public class Student { private Long id; private String name; private int age; private Address address;
} |
配置文件
1.通过配置Bean标签来完成对象的管理
2.id:对象名
3.class:对象的模板类。(交给ioc管理的类必须要有无参构造方法,spring底层通过反射机制来创建对象,调用的是无参构造)
4.对象的成员变量通过property标签完成赋值
5.name:成员变量名,value成员变量值(基本数据类型,String可以直接赋值,引用类型通过ref注入)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?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.wiggin.entity.Student"> <property name="id" value="1"></property> <property name="name" value="wiggin"></property> <property name="age" value="22"></property> <property name="address" ref="address"></property> </bean> <bean id="address" class="com.wiggin.entity.Address"> <property name="id" value="2"></property> <property name="name" value="vivian"></property> </bean> </beans> |
IOC的底层原理
1.读取配置文件,解析XML
2.通过反射机制,实例化配置文件中所配置文件中所有的Bean
通过运行时类获取bean
1 2 3 | ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) applicationContext.getBean("student"); System.out.println(student); |
这种方式存在一个问题,配置文件中一个数据类型的对象只能有一个实例。否则会抛出异常,因为没有唯一的bean
通过有参构造来创建bean
1 2 3 4 5 6 | <bean id="student3" class="com.wiggin.entity.Student"> <constructor-arg name="id" value="3"></constructor-arg> <constructor-arg name="name" value="bob"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="address" ref="address"></constructor-arg> </bean> |
其中name可以实省略,但是可以通过下标来确定位置,但顺序必须与成员变量顺序一致
1 2 3 4 5 6 | <bean id="student3" class="com.wiggin.entity.Student"> <constructor-arg index="0" value="3"></constructor-arg> <constructor-arg index="1" value="bob"></constructor-arg> <constructor-arg index="2" value="18"></constructor-arg> <constructor-arg index="3" ref="address"></constructor-arg> </bean> |
给bean注入集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <bean id="student" class="com.wiggin.entity.Student"> <property name="id" value="1"></property> <property name="name" value="wiggin"></property> <property name="age" value="22"></property> <property name="addresses"> <list> <ref bean="address"></ref> <ref bean="address2"></ref> </list> </property> </bean> <bean id="address" class="com.wiggin.entity.Address"> <property name="id" value="1"></property> <property name="name" value="科技路"></property> </bean> <bean id="address2" class="com.wiggin.entity.Address"> <property name="id" value="2"></property> <property name="name" value="高新区"></property> </bean> |
Scope作用域
Spring管理bean是根据scope来生成的,表示bean的作用域,共有4种,默认是单例模式
1)singleton:单例,表示通过Spring容器获取的bean是唯一的,
2)prototype:原型,表示通过Spring容器获取的bean是不同的
3)request:请求,表示一次Http请求内有效
4)session:回话,表示在一个用户会话内有效
request和session是适用于web项目,大多数使用单例和原型。
prototype模式当业务代码获取Ioc容器中的bean时(getbean),Spring才去调用无参构造来创建对应的bean。创建一个bean开辟一个空间,避免bean的利用率低的问题,但是也浪费了地址空间。
1 2 3 | <bean id="student2" class="com.wiggin.entity.Student" scope="prototype"> <property name="name" value="vivian"></property> </bean> |
singleton模式当ioc容器创建时(new ClassPathXmlApplicationContext("applicationContext.xml"))时,bean已经被创造。多个bean指向同一个地址更加节省空间,但是bean在被创建后的利用率低。
Spring的继承
与java的继承不同,java是类层面的继承,子类可以继承父类的父类的内部结构信息,Spring是对象层面的继承,子对象可以继承父对象的属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <bean id="student2" class="com.wiggin.entity.Student"> <property name="name" value="vivian"></property> <property name="id" value="2"></property> <property name="age" value="22"></property> <property name="addresses"> <list> <ref bean="address"></ref> <ref bean="address2"></ref> </list> </property> </bean> <bean id="stu" class="com.wiggin.entity.Student" parent="student2"> <property name="name" value="wzxsb"></property> </bean> |
Spring的继承的关注点在于具体的对象,而不在于类,即不同的两个类的实例化对象可以完成继承,前提是子对象的属于一定是包括父对象的属性。
1 | <bean id="user" class="com.wiggin.entity.User" parent="student2"></bean> |
Spring的依赖
与继承类似,依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定要先被创建。
1 2 3 | <bean id="student" class="com.wiggin.entity.Student" ></bean>
<bean id="user" class="com.wiggin.entity.User" depends-on="student"></bean> |
Spring的p命名空间
p命名空间实对ioc/DI的简化操作,使用p命名空间可以更加方便的完成bean的配置以及bean之间的依赖注入
1 2 3 4 5 6 7 8 9 10 | <?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="student" class="com.wiggin.entity.Student" p:id="1" p:name="张三" p:age="22" p:address-ref="address"></bean> <bean id="address" class="com.wiggin.entity.Address" p:id="2" p:name="科技路"></bean> </beans> |
Spring的工厂方法
ioC通过工厂模式创建bean的方式有两种:
1)静态工厂方法
2)实例工厂方法
静态工厂方法:工厂不需要实例化,直接使用工厂方法
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.wiggin.entity;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @AllArgsConstructor @NoArgsConstructor public class Car { private long id; private String name; } |
------
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package com.wiggin.factory;
import com.wiggin.entity.Car;
import java.util.HashMap; import java.util.Map;
public class StaticFactory { private static Map<Long, Car> carMap; // Long 是id,Car是返回容器 static{ carMap = new HashMap<Long, Car>(); carMap.put(1L,new Car(1L,"sf")); carMap.put(2L,new Car(2L,"sb")); }
public static Car getCar(long id){ // 根据id提取car信息 return carMap.get(id); } } |
------
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.wiggin.test;
import com.wiggin.entity.Car; import com.wiggin.factory.StaticFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test3 { public static void main(String[] args) { // 传统方法 /*Car car = StaticFactory.getCar(1L); System.out.println(car);*/ // ioc工厂模式 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-factory.xml"); Car car = (Car)applicationContext.getBean("car"); System.out.println(car); } } |
------
1 2 3 4 5 6 7 8 9 10 11 | <?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--> <bean id="car" class="com.wiggin.factory.StaticFactory" factory-method="getCar"> <constructor-arg value="2"></constructor-arg> </bean> </beans> |
实例工厂方法:工厂需要实例化,且工厂方法要依赖工厂
1 2 3 4 5 6 | <!-- 配置实例工厂 bean --> <bean id="carFactory" class="com.wiggin.factory.InstanceCarFactory"></bean> <!-- 配置实例工厂创建 car --> <bean id="car2" factory-bean="carFactory" factory-method="getCar"> <constructor-arg value="2"></constructor-arg> </bean> |
IoC自动装载(Autowire)
ioC负责创建对象,DI负责完成对象的依赖注入,通过配置Property标签的ref属性来完成,同时Spring提供了另一种更加间接的依赖注入方法,即自动装载,不用手动配置property,IoC容器会自动选择bean完成注入。
自动装载有两种方式:
1)byname:通过属性名自动装载
2)bytype:通过属性类型装载
byName:通过属性名自动装载
1 2 3 4 5 6 7 8 9 | <bean id="car" class="com.wiggin.entity.Car"> <property name="id" value="1"></property> <property name="name" value="宝马"></property> </bean> <bean id="person" class="com.wiggin.entity.Person" autowire="byName"> <property name="id" value="1"></property> <property name="name" value="wiggin"></property>
</bean> |
byType:通过属性类型装载
1 2 3 4 5 | <bean id="person2" class="com.wiggin.entity.Person" autowire="byType"> <property name="id" value="1"></property> <property name="name" value="wiggin"></property>
</bean> |
Spring的AOP
AOP:Aspect Oriented Programming 面向切面编程
AOP 优点:
- 降低模块之间的耦合度
- 使得系统更容易扩展
- 更好的代码服用
- 非业务代码更加集中,不分散,使于统一管理
- 业务代码更加简介纯粹,不掺杂其他代码的影响
总结:AOP是对面向对象编程的补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程,将不同方法的同一个位置抽象成为一个企鹅面对象,对该企鹅面对象进行编程,这就是AOP。
如何使用?
1)创建一个maven工程,pom.xml添加
1 2 3 4 5 6 7 8 9 10 11 12 13 | <dependencies>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.0.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.0.7.RELEASE</version> </dependency> </dependencies> |
2)创建一个计算器接口Cal,定义四则运算。
1 2 3 4 5 6 | package com.wiggin.utils; public interface Cal { public int add(int num1,int num2); public int sub(int num1,int num2); public int mul(int num1,int num2); public int div(int num1,int num2); } |
3)创建接口的实现类impl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package com.wiggin.utils.impl; public class CalImpl implements Cal {
public int add(int num1, int num2) { System.out.println("add方法参数是["+num1+","+num2+"]"); int result = num1 + num2; System.out.println("add方法的结果是:"+result); return num1 + num2; }
public int sub(int num1, int num2) { int result = num1 - num2; System.out.println("sub方法参数是["+num1+","+num2+"]"); System.out.println("sub方法结果是:"+result); return result; }
public int mul(int num1, int num2) { int result = num1 * num2; System.out.println("mul方法参数是["+num1+","+num2+"]"); System.out.println("mul方法结果是:"+result); return result; }
public int div(int num1, int num2) { int result = num1 / num2; System.out.println("div方法参数是["+num1+","+num2+"]"); System.out.println("div方法结果是:"+result); return result; } } |
上述代码种,日志信息和业务逻辑的耦合性很高,不利于系统的维护,使用AOP可以进行优化,如何来实现AOP?实现动态代理方式来实现。
给予业务代码找一个代理,打印日志信息的工作交给代理来做,这样的话业务代码就只需要关注自身的业务即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package com.wiggin.utils; public class MyInvocationHandler implements InvocationHandler {
// 接收委托对象 private Object object = null;
// 返回代理对象(委托对象传入代理对象),用这个类创建动态代理类 public Object bind(Object object){ this.object = object; /*Proxy.newProxyInstance:返回代理实类, object.getClass()获得委托对象的运行实类, .getClassLoader()获取类加载器,生成动态实类 object.getClass().getInterfaces()委托对象的功能代理类必须全部拥有,根据它创建功能一样的代理类 this:通过当前类创建对象*/ // return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); }
@Override // 打印日志结果 /* method为代理类实现的方法 method.invoke(this.object,objects)通过反射机制调用,this.object是委托对象,objects为传入参数
*/ public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println(method.getName()+"方法参数是"+ Arrays.toString(objects)); // 委托对象调用自己的方法 Object result = method.invoke(this.object,objects); System.out.println(method.getName()+"方法的结果是:"+result); return result;
}
} |
以上是通过动态代理来实现AOP的过程,过程复杂,不好理解,Spring框架对AOP进行了封装,使用Spring框架可以用面向对象的思想来实现AOP。
Spring框架种不需要创建InvocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象种完成即可,Spring框架底层会自动根据切面类以及目标类生成代理对象。
LoggerAspect类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package com.wiggin.aop; @Aspect // 成为切面对象,给予功能 @Component // 将LoggerAspect对象在ioc里面配置 public class LoggerAspect { // 传入实体类,切割实体类种的所有方法,在执行业务之前,*为方法的通配符,(..)为参数的通配符 // @Before表示执行的具体时机 @Before("execution(public int com.wiggin.utils.impl.CalImpl.*(..))") // JoinPoint joinPoint获得目标方法的相关信息 public void before(JoinPoint joinPoint){ // 获取方法joinPoint.getSignature(),方法名getName() String name = joinPoint.getSignature().getName();
//获取参数joinPoint.getArgs() String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name + "方法的参数是:" + args);
} } |
注意CaLimpl也需要@Component注解,交给IoC容器进行管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package com.wiggin.utils.impl; @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-aop.xml种配置AOP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- 自动扫描 寻找component注解--> <context:component-scan base-package="com.wiggin"></context:component-scan>
<!-- aspect注解生效,为目标类自动生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans> |
loggerAspect实现更多业务功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package com.wiggin.aop; @Aspect // 成为切面对象,给予功能 @Component // 将LoggerAspect对象在ioc里面配置 public class LoggerAspect { // 传入实体类,切割实体类种的所有方法,在执行业务之前,*为方法的通配符,(..)为参数的通配符 // @Before表示执行的具体时机 @Before("execution(public int com.wiggin.utils.impl.CalImpl.*(..))") // JoinPoint joinPoint获得目标方法的相关信息 public void before(JoinPoint joinPoint){ // 获取方法joinPoint.getSignature(),方法名getName() String name = joinPoint.getSignature().getName();
//获取参数joinPoint.getArgs() String args = Arrays.toString(joinPoint.getArgs()); System.out.println(name + "方法的参数是:" + args);
} @After("execution(public int com.wiggin.utils.impl.CalImpl.*(..))") public void after(JoinPoint joinPoint){ // 获取方法joinPoint.getSignature(),方法名getName() String name = joinPoint.getSignature().getName(); System.out.println(name + "方法执行了");
} @AfterReturning(value = "execution(public int com.wiggin.utils.impl.CalImpl.*(..))",returning = "result") // 将result交给了 @AfterReturning,然后给了public void afterReturning public void afterReturning(JoinPoint joinPoint,Object result){ // 获取方法名 String name = joinPoint.getSignature().getName(); System.out.println(name + "方法执行结果是:"+result); }
@AfterThrowing(value = "execution(public int com.wiggin.utils.impl.CalImpl.*(..))",throwing = "expection") // 将result交给了 @AfterReturning,然后给了public void afterReturning public void afterThrowing(JoinPoint joinPoint,Exception expection){ // 获取方法名 String name = joinPoint.getSignature().getName(); System.out.println(name + "方法执行异常:"+expection); } } |
IoC调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package com.wiggin.test;
import com.wiggin.utils.Cal; import com.wiggin.utils.MyInvocationHandler; import com.wiggin.utils.impl.CalImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2 { public static void main(String[] args) { // 加载配置文件 ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring.xml");
// 获取代理对象 Cal proxy = (Cal) applicationContext.getBean("test"); proxy.add(1,1); proxy.div(6,0);
} |
切面:横切关注点被模块化的抽象对象(对象中相同的非业务代码)
通知:切面对象完成的工作(非业务代码)
目标:被通知的对象,即被切面的对象(非业务代码存在的类)
代理:切面、通知、目标混合之后的对象(一个代理实现非业务代码功能的类)
连接点:通知要插入业务代码的具体位置(代理和目标之间的联系)
切点:AOP通过切点定位到连接点