DI是IoC的技术实现
DI:依赖注入,(Dependence injection)创建对象是由容器创建,只需要提供对象名即可实现对象的创建。
DI是通过依赖注入给属性赋值。
String使用的DI实现了IOC功能,底层是使用了反射机制。
spring链接:CLICK-HERE
Beans.xml
测试spring的IoC功能(自动创建对象):
创建maven工程,配置pom文件,使用spring最新版找不到jar包,只好使用spring-5.2.5版本的spring,顺利找到jar,进行下一步的操作。
spring依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
spring自动创建对象的时机:
并且会创建所有的对象,并把创建好的对象放到容器的Map集合中储存,以键值对的形式存在,Key为id,Value为new出来的对象。取出对象是依据id取。
取出对象: ac.getBean(id值)
注意这里应该有强制类型转换,具体转换的类型依据取出的对象的类型。
获取容器中对象信息的API
- 获取容器中定义的对象的数量:
int nums = ac.GetBeanDefinationCount()
- 容器中每个对象的名称:
String names[] = ac.GetBeanDefinationNames()
创建非自定义的类的对象
例如要创建一个date类型的对象,只需要在beans.xml文件中声明一下即可。
<bean id="myDate" class="java.util.Date"/>
注明全限定名称即可创建。
spring创建对象,默认调用的是无参数构造方法。
DI
- DI的语法分类
DI的分类
简单类型的set注入:
在applicationContext.xml配置文件中的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="myStudent" class="com.tommy.ba01.Student">
<property name="name" value="张三"/>
<property name="age" value="16"/>
</bean>
</beans>
注意,如果要做赋值的类中没有set方法,那么将会报错,无法实现属性的赋值。
引用类型的set注入:
语法代码如下:
<?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="myStudent" class="com.tommy.ba02.Student">
<property name="name" value="张三"/>
<property name="school" ref="mySchool"/>
<property name="age" value="18"/>
</bean>
<bean id="mySchool" class="com.tommy.ba02.School">
<property name="name" value="PKU"/>
<property name="address" value="BeiJing"/>
</bean>
</beans>
注意,再给引用类型传值时,不能用value吗,用ref,不然会报错误:NoSuchMethodError
构造注入
spring调用类有参数的构造方法,在创建对象的同时,给Constructor的属性赋值。
构造注入使用<Constructor-arg>
标签,一个标签表示一个构造方法中的一个参数
Constructor-arg标签属性:
- name:表示构造方法的形参名
- index:表示构造方法的参数位置,从左往右依次是:0, 1, 2, ⋯ \cdots ⋯
- 构造方法的形参类型是简单类型的使用value
- 构造方法的形参类型时引用类型的使用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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myStudent" class="com.tommy.ba03.Student">
<constructor-arg name="school" ref="mySchool"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="name" value="张三"/>
</bean>
<bean id="mySchool" class="com.tommy.ba03.School">
<property name="name" value="PKU"/>
<property name="address" value="BeiJing"/>
</bean>
</beans>
执行步骤:
(相当于按照标签的个数自动重载了)
引用类型的自动注入
关键字:autowire="byType/byName "
spring框架根据某些规则给引用类型赋值。
使用的规则:
- byName(按名称注入):JAVA类中引用类型的属性名和spring配置文件(容器) 中的
<bean>
的id相同且数据类型是一样的,这样可完成引用类型的自动赋值。
bean属性: autowire="byName"
使用实例:
<?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="myStudent" class="com.tommy.ba03.Student" autowire="byName">
<constructor-arg name="school" ref="school"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="name" value="张三"/>
</bean>
<bean id="school" class="com.tommy.ba03.School">
<property name="name" value="北京职业技术学院"/>
<property name="address" value="山东菏泽曹县"/>
</bean>
</beans>
代码关系图解:
- byType(按类型注入):java类中的引用数据类型和spring的
<bean>
中的引用类型的数据类型时同源关系,这样的可以实现赋值。
同源关系:
- java类中的引用数据类型和
<bean>
中的class的值得的数据类型是一样的。 - java类中的引用数据类型和
<bean>
中的class的数据类型是父子类关系的。 - java类中的引用数据类型和
<bean>
中的class的数据类型是接口和实现类关系的。
注意:在byType中,在xml配置文件中声明的bean只能有一个符合条件的,多余一个是错误的。
代码实例:
spring-student.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="myStudent" class="com.tommy.ba04.Student" autowire="byType">
<property name="age" value="18"/>
<property name="name" value="张三"/>
</bean>
</beans>
spring-school.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="school" class="com.tommy.ba04.School">
<property name="name" value="西北工业大学"/>
<property name="address" value="陕西西安"/>
</bean>
</beans>
total.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">
<import resource="classpath:resource04/spring-school.xml"/>
<import resource="classpath:resource04/spring-student.xml"/>
</beans>
total 的作用是整合配置文件
注解:byName 的注解:执行过程是:在读取ApplicationContext.xml配置文件的autowired="byName"时,到此类中找到引用类型的变量,将此引用类型的名字和配置文件中的bean标签的id值去对比,直到找到相同的讲这个bean标签中的值自动付给引用类型。
byType注解:执行过程:在读取到ApplicationContext.xml中的bean标签中有autowired="byType"时,自动到类中寻找所有的引用类型,读取类型和配置文件中的bean标签中的class进行对比,直到找到类型是同源关系的,spring将进行自动复制。
注解:在使用自动赋值后不用再主bean标签中写入property id="" ref=""了
使用注解
注解的使用步骤:
组件扫描器(component-scan)
basepackage:指定注解在项目中的包名
component-scan工作方式:spring会遍历扫描basepackage指定的包,以及其中的子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。
扫描多个包的三种方式:
@value:简单类型的属性赋值
语法:@value(value = " ")
注意这里的属性值value是String类型的,表示简单类型的属性值
定义的位置:
- 在属性定义的上面,不用set方法,推荐使用。
- 在set方法的上面。
测试代码:
package com.tommy.ba02;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "myStudent02")
public class Student {
@Value(value = "张三")
private String name;
@Value(value = "99")
private Integer age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
引用类型的赋值
使用@AutoWired:
默认为byType的匹配模式。
- 在属性的上面,不需要set方法
- 在set方法的上面
实例源代码:
package com.tommy.ba03;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "myStudent03")
public class Student {
@Value(value = "张三")
private String name;
@Value(value = "99")
private Integer age;
@Autowired
private School school;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
package com.tommy.ba03;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//注意这里的mySchool代表的就是使用bean标签时的id值
@Component(value = "mySchool")
//这样容器中就有了school类型的数据了。
//按类型自动注入就会自动在容器中匹配这个School类型
public class School {
@Value(value = "北京")
private String address;
@Value(value = "北大清华")
private String name;
public void setAddress(String address) {
this.address = address;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "School{" +
"address='" + address + '\'' +
", name='" + name + '\'' +
'}';
}
}
在配置文件中使用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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.tommy.ba03" />
//这样在容器中一样创建了school对象。和使用注解的方式一样。
<bean id="mySchool" class="com.tommy.ba03.School">
<property name="name" value="TsingHuaUniversity"/>
<property name="address" value="BeiJing"/>
</bean>
</beans>
byName:
- 在属性上面加入@Autowired
- 在后面加上@Qualifier(value=“bean的id”)
示例代码:
package com.tommy.ba04;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "myStudent04")
public class Student {
@Value(value = "张三")
private String name;
@Value(value = "99")
private Integer age;
@Autowired
@Qualifier(value = "mySchool") //这两个注解没有先后顺序。
private School school;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
}
探讨@Autowire的属性required:
required, 是一个boolean类型的,默认值为true
required = true:表示引用类型的赋值失败,程序报错,并终止执行。
required = false:引用类型如果赋值失败,程序正常执行,引用类型的 结果赋值为null
使用时最好使用true,避免空指针异常,便于程序的调试。
@resource
来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用他给引用类型赋值,使用的也是自动注入原理,支持byName, byType, 默认是byName
使用位置:
- 在属性定义的上面,无需使用set方法,推荐使用
- 在set方法的上面
原则是默认为byName的方式,该方式赋值失败后,在使用byType的复制方式。
如果只想使用byName的赋值方式,如下图:
问题:一使用@Resource注解就报错,该导入的javax依赖也已经导入了,就是一直报错,心态崩了。
"C:\Program Files\Java\jdk-16.0.1\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=64921:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\plugins\junit\lib\junit-rt.jar;C:\Users\TOMMY\IdeaProjects\untitled3\target\test-classes;C:\Users\TOMMY\IdeaProjects\untitled3\target\classes;C:\Users\TOMMY\.m2\repository\org\apache\wicket\wicket\1.3.2\wicket-1.3.2.jar;C:\Users\TOMMY\.m2\repository\org\slf4j\slf4j-api\1.4.2\slf4j-api-1.4.2.jar;C:\Users\TOMMY\.m2\repository\org\slf4j\slf4j-log4j12\1.4.2\slf4j-log4j12-1.4.2.jar;C:\Users\TOMMY\.m2\repository\log4j\log4j\1.2.14\log4j-1.2.14.jar;C:\Users\TOMMY\.m2\repository\junit\junit\4.13\junit-4.13.jar;C:\Users\TOMMY\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\TOMMY\.m2\repository\org\mortbay\jetty\jetty\6.1.4\jetty-6.1.4.jar;C:\Users\TOMMY\.m2\repository\org\mortbay\jetty\servlet-api-2.5\6.1.4\servlet-api-2.5-6.1.4.jar;C:\Users\TOMMY\.m2\repository\org\mortbay\jetty\jetty-util\6.1.4\jetty-util-6.1.4.jar;C:\Users\TOMMY\.m2\repository\org\mortbay\jetty\jetty-management\6.1.4\jetty-management-6.1.4.jar;C:\Users\TOMMY\.m2\repository\mx4j\mx4j\3.0.1\mx4j-3.0.1.jar;C:\Users\TOMMY\.m2\repository\mx4j\mx4j-tools\3.0.1\mx4j-tools-3.0.1.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;C:\Users\TOMMY\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 com.tommy.MyTest03,test03
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [applicationContext.xml]; nested exception is java.lang.NoSuchMethodError: 'void org.slf4j.spi.LocationAwareLogger.log(org.slf4j.Marker, java.lang.String, int, java.lang.String, java.lang.Object[], java.lang.Throwable)'
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:415)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:305)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:224)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:195)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:257)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:128)
at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:94)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:637)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85)
at com.tommy.MyTest03.test03(MyTest03.java:14)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.NoSuchMethodError: 'void org.slf4j.spi.LocationAwareLogger.log(org.slf4j.Marker, java.lang.String, int, java.lang.String, java.lang.Object[], java.lang.Throwable)'
at org.apache.commons.logging.LogAdapter$Slf4jLocationAwareLog.trace(LogAdapter.java:482)
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.registerDefaultFilters(ClassPathScanningCandidateComponentProvider.java:211)
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.<init>(ClassPathBeanDefinitionScanner.java:166)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.createScanner(ComponentScanBeanDefinitionParser.java:132)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.configureScanner(ComponentScanBeanDefinitionParser.java:103)
at org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(ComponentScanBeanDefinitionParser.java:89)
at org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(NamespaceHandlerSupport.java:74)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1391)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1371)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:179)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:149)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:96)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:509)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:389)
... 39 more
Process finished with exit code -1
这报错服了。。。。
注解:如果在项目中属性的值需频繁的修改,建议使用xml配置文件的形式赋值,改动小用注解赋值。
动态代理
可以在程序执行的过程中创建代理对象,通过代理对象执行方法给目标类的方法增加额外的功能。
实现步骤:
- 创建目标类
- 创建InvocationHandler接口的实现类,在这个类中实现给目标方法增加功能。
- 使用jdk中的类proxy创建代理对象,实现创建对象的能力。
代码实例:
package com.tommy.handler;
import com.tommy.util.ServiceTools;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyIncationHandler implements InvocationHandler {
private Object target;
public MyIncationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
ServiceTools.date();
res = method.invoke(target, args); // 代表的是SomeServiceImpl中的方法doSome()或者doOther()
ServiceTools.submit();
return res;
}
}
可以在动态代理模块中实现方法的执行选择等逻辑算法的实现,而在不表方法中的代码并不需要进行任何修改,这样最大限度的实现了解耦合。便于代码的分布式管理。
AOP
动态代理的范式形式,规范化的动态代理机制。面向切面编程
实现方式:
- jdk动态代理:使用jdk中的proxy, method, InvocationHandler创建代理对象,jdk动态代理要求目标类必须实现接口。
- cglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类创建子类,子类就是代理对象,要求目标类不能是final的,方法也不能是final的。
使用的AOP:开源AOP框架aspectJ
spring框架集成了aspectJ框架,可实现通过spring直接使用aspectJ的功能。
aspectJ实现aop功能的两种方式:
- 使用xml配置文件
- 使用注解(5个)
切入点表达式:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
注解:
modifiers-pattern
访问权限类型
ret-type-pattern
返回值类型
declaring-type-pattern
包名类名
name-pattern(param-pattern)
方法名(参数类型和参数个数)
throws-pattern
抛出异常类型
?
表示可选内容
使用通配符:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后面必须跟“
∗
*
∗”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
添加依赖:
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- aspectJ依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
使用AOP实现一个前置通知:
- 添加maven依赖:
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- aspectJ dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
-
创建目标类,创建接口和他的实现类。
目标:在不改动源代码的基础上为方法增加功能。 -
创建切面类:在类的上面加上
@aspect
,再类中定义方法,这个方法就是切面要执行的功能代码,要在功能方法上面加上通知注解:例如@Before
等通知注解。并且指定切入点表达式execution
。 -
创建spring的配置文件
applicationContext.xml
,声明对象,把对象交给容器统一管理
1)生成目标对象
2)生成切面类对象
3)自动代理生成器,用于完成代理对象的自动创建。 -
最后创建测试类进行从容器中获取对象进行代码测试即可。
指定通知方法中的参数:
JoinPoint
:
可以在通知方法中获取方法执行时的信息,参数JoinPoint
的值由框架赋予,必须是第一个位置的参数。
@Before(value = "execution(public void com.bjpowernode.ba01.SomeServiceImpl.doSome(String, Integer))")
public void myBefore(JoinPoint jp) {
System.out.println(jp.getSignature());
Object args [] = jp.getArgs();
for(Object arg : args) {
System.out.println("参数 = " + arg);
}
System.out.println(jp.getSignature().getName());
System.out.println("前置通知:在目标方法执行之前输出时间: " + new Date());
}
注意:获取参数的方法的返回值为Object类型的数组:Object[] getArgs();
后置通知:
后置通知在切面类的上面加上@AfterReturning(value="execution(...)", returning="res")
,res要和切面方法的参数变量名一样才行,用处是获取到目标方法的返回值,可以拿到返回值,根据返回值做业务方法。
@AfterReturning(value = "execution(String com.tommy.ba02.SomeServiceImpl.myStudent(String, Integer))",
returning = "res1")
public void myAfterReturning(Object res1) {
System.out.println("这是后置通知");
}
切面方法的定义要求:
- public
- 方法没有返回值
- 方法名自定义
- 方法有参数:
推荐使用Object,参数名自定义
后置通知@Afterreturning有两个属性:
- value,切入点表达式
2.returning表示自定义的变量 ,表示目标方法的返回值的,自定义的变量名必须和通知方法的形参名一样。
环绕通知
功能最强大的通知
环绕通知方法的定义格式:
- public
- 必须有一个返回值,推荐用Object
- 方法名称自定义
- 方法有参数,固定的参数 ProceedingJoinPoint
Pointcut注解:
定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的,可以使用@PointCut
属性:切入点表达式
位置:在自定义方法的上面
当使用此注解在一个方法的上面时,此时这个方法的名称就是切入点表达式的别名,其他通知中,value属性就可以使用这个方法名称,代替切入点表达式了。
自定义的方法中不需要代码。
一般定义为私有的。
如果目标方法没有接口,那么默认使用的是CGLIB动态代理。
有接口也能用CGLIB动态代理
如果你期望目标类有接口,使用cglib动态代理,在配置文件中加入:
<aop:aspectj-autoproxy proxy-target-class="true" />
MyBatis和Spring的集成使用:
原理是使用了spring 的ioc功能。
用的技术是:ioc 。
为什么ioc:能把mybatis和spring集成在一起,像一个框架, 是因为ioc能创建对象。
可以把mybatis框架中的对象交给spring统一创建, 开发人员从spring中获取对象。
开发人员就不用同时面对两个或多个框架了, 就面对一个spring
mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);
List students = dao.selectStudents();
要使用dao对象,需要使用getMapper()方法,
怎么能使用getMapper()方法,需要哪些条件
1.获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
2.创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象
需要SqlSessionFactory对象, 使用Factory能获取SqlSession ,有了SqlSession就能有dao , 目的就是获取dao对象
Factory创建需要读取主配置文件
我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。
主配置文件:
1.数据库信息
<environment id="mydev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
</dataSource>
- mapper文件的位置
<mappers>
<mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
<!--<mapper resource="com/bjpowernode/dao/SchoolDao.xml" />-->
</mappers>
通过以上的说明,我们需要让spring创建以下对象
1.独立的连接池类的对象, 使用阿里的druid连接池
2.SqlSessionFactory对象
3.创建出dao对象