文章目录
ClassPathXmlApplicationContext工作原理
创建一个ClassPathXmlApplicationContext类
package com.qx;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class ClassPathXmlApplicationContext {
private static Map<String,Object> beanMap = new HashMap<>();
private String filePath;
public ClassPathXmlApplicationContext(String filePath){
this.filePath = filePath;
load();
}
private void load(){
try {
//获取resources目录下的.xml文件 beans.xml
String path = ClassPathXmlApplicationContext.class.getClassLoader().getResource(filePath).getPath();
//将我们的路径编码设置为utf-8 否则会出现乱码
path = URLDecoder.decode(path,"utf-8");
//解析path 修改为正常的路径 D:/Java学习/Spring/spring_00_IOD/target/classes/beans.xml
Document document = Jsoup.parse(new File(path), "utf-8");
//使用document里边的方法 通过获取元素标签的方法 取得 beans.xml文件中的bean标签
//<bean class="com.qx.dao.impl.UserDaoImpl2" id="userDao"/>
Elements beans = document.getElementsByTag("bean");
//判断获取到的beans是否为空,如果为空则不执行
if (beans != null&& beans.size()>0){
for (int i = 0;i<beans.size();i++){
//遍历所有的bean标签
Element bean = beans.get(i);
//获取bean标签中的class和id值
//获取bean标签中的属性 bean.attr("class"); 获取标签中的class属性
// bean.attr("id"); 获取标签中id属性
String className = bean.attr("class");//com.qx.dao.impl.UserDaoImpl* 全类名
String id = bean.attr("id");//userDao
//通过反射创建对应类的对象 反射可以使用当前文件下的绝对路径(com.qx.dao.impl.UserDaoImpl*) 获取到这个类
Class<?> clazz = Class.forName(className);
Constructor<?> constructor = clazz.getConstructor();//获取这个类里边的构造方法
Object obj = constructor.newInstance();//调用里边的构造方法 去构造对象 即new出这个构造方法
//把id作为key 创建出来的对象作为value存入map中
beanMap.put(id,obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据id从容器中获取对应的对象
* @param id
* @return
*/
public Object getBean(String id){
return beanMap.get(id);//根据id从map中取得value
}
}
创建UserDao
package com.qx.dao;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public interface UserDao {
String getUserNameById(Integer id);
}
创建UserDao实现类
package com.qx.dao.impl;
import com.qx.dao.UserDao;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class UserDaoImpl1 implements UserDao {
@Override
public String getUserNameById(Integer id) {
return "qx111";
}
}
package com.qx.dao.impl;
import com.qx.dao.UserDao;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class UserDaoImpl2 implements UserDao {
@Override
public String getUserNameById(Integer id) {
return "qx222";
}
}
创建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 class="com.qx.dao.impl.UserDaoImpl1" id="userDao"/>-->
<bean class="com.qx.dao.impl.UserDaoImpl2" id="userDao"/>
</beans>
测试
package com.qx;
import com.qx.dao.UserDao;
import com.qx.dao.impl.UserDaoImpl2;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class Demo {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");
//获取bean中的id属性值
UserDao UserDao = (UserDao) classPathXmlApplicationContext.getBean("userDao");
System.out.println(UserDao.getUserNameById(1));
// UserDao userDao = new UserDaoImpl1();
}
}
01、IOC:控制反转
什么是IOC?
举个例子,我们有一个UserDao接口类,并且有三个UserDao实现类,分别是MysqlUserDaoImpl、OracleUserDaoImpl、UserDaoImpl,都实现UserDao接口,接下来我们编写service层的UserService接口,并且编写UserService实现类UserServiceImpl,如果根据传统的方法去调用dao层的三个实现类来实现其中的方法
我们需要写三遍以下代码
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new MysqlUserDaoImpl());
userService.getUser();
userService.setUserDao(new OracleUserDaoImpl());
userService.getUser();
userService.setUserDao(new UserDaoImpl());
userService.getUser();
但是现在,我们不用传统的方法去调用,而是在UserService实现类中set dao层中的UserDao接口类
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
然后将我们的dao层中三个接口实现类通过bean交给spring容器托管,并且将Service的实现类也交给spring容器接管
<?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.0.xsd">
<bean id="mysqlImpl" class="com.kk.dao.MysqlUserDaoImpl"/>
<bean id="oracleImpl" class="com.kk.dao.OracleUserDaoImpl"/>
<bean id="userDaoImpl" class="com.kk.dao.UserDaoImpl"/>
<bean id="UserServiceImpl" class="com.kk.service.UserServiceImpl">
<!-- ref 引用Spring容器中创建好的对象
value 具体的值,基本数据类型
-->
<!--在UserServiceImpl创建UserDao,将UserDao引入 因此UserServiceImpl有这么一个UserDao的属性 name="userDao",使用ref引用UserDao的三个实现类-->
<property name="userDao" ref="mysqlImpl"/>
<!-- <property name="userDao" ref="oracleImpl"/>-->
<!-- <property name="userDao" ref="userDaoImpl"/>-->
</bean>
</beans>
这时如果我们想要使用dao层中三个接口实现类中的某一个实现类,我们只需要 <property name="userDao" ref="userDaoImpl"/> 使用这个工具的ref选择我们想要实现的实现类
前提是我们必须在service接口实现类中有set dao的接口
package com.kk.service;
import com.kk.dao.UserDao;
public class UserServiceImpl implements UserService{
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
代码如下:
UserDao
package com.kk.dao;
public interface UserDao {
public void getUser();
}
MysqlUserDaoImpl
package com.kk.dao;
public class MysqlUserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Mysql获取用户数据");
}
}
OracleUserDaoImpl
package com.kk.dao;
public class OracleUserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Oracal获取用户数据");
}
}
UserDaoImpl
package com.kk.dao;
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
UserService
package com.kk.service;
public interface UserService {
public void getUser();
}
UserServiceImpl
package com.kk.service;
import com.kk.dao.UserDao;
public class UserServiceImpl implements UserService{
private UserDao userDao;
//IOC 控制反转
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
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-3.0.xsd">
<bean id="mysqlImpl" class="com.kk.dao.MysqlUserDaoImpl"/>
<bean id="oracleImpl" class="com.kk.dao.OracleUserDaoImpl"/>
<bean id="userDaoImpl" class="com.kk.dao.UserDaoImpl"/>
<bean id="UserServiceImpl" class="com.kk.service.UserServiceImpl">
<!-- ref 引用Spring容器中创建好的对象
value 具体的值,基本数据类型
-->
<!-- 想要使用哪一个实现类ref就指向哪一个 -->
<property name="userDao" ref="mysqlImpl"/>
</bean>
</beans>
测试:
public class Test {
public static void main(String[] args) {
//获取ApplicationContext 拿到Spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//拿到容器之后 需要什么就get即可
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
userServiceImpl.getUser();
}
}
以上便是IOC的思想,化繁为简,使用传统的去实现这个功能,需要不断的改变测试类的代码,然而使用IOC控制反转,只需改bean.xml中的代码即可
02、什么是bean
在Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bean是一个由Spring IoC容器实例化、组装和管理的对象。
我们总结如下:
1.bean是对象,一个或者多个不限定
2.bean由Spring中一个叫IoC的东西管理
3.我们的应用程序由一个个bean构成
比如我们建立一个实体类Hello
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
private String str;
}
将这个类在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-3.0.xsd">
<bean id="hello" class="com.kk.pojo.Hello">
<!-- set注入 获取Hello中的属性str 并且给str赋值Spring-->
<property name="str" value="Spring"/>
</bean>
</beans>
使用Spring创建对象,在Spring中 这些都称为Bean
类型 变量名 = new 类型
Hello hello = new Hello()
bean id = new 对象()
id=变量名
class = new 的对象((Hello))
property 相当于给对象中的属性设置值
其核心就是,给属性str使用set进行赋值
public void setStr(String str) {
this.str = str;
}
测试:
public class Test {
public static void main(String[] args) {
//获取Spring的上下文对象 获取其中resources目录下的beans.xml文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello"); //获取bean中参数id为hello
System.out.println(hello.toString());
}
}
获取Spring的上下文对象,使用getBean获得bean中的id,即可获得Hello这个对象并且获得赋给ta的值
03、DI注入以及c、p命名空间
编写实体类
Address
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String address;
}
Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
}
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}
将实体类都注入到bean中,编程一个个bean
p命名空间、c命名空间
userbean.xml 用于将实体类User注入到spring容器中,交给spring托管
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- p 命名空间注入,可以直接注入属性的值 property-->
<bean id="user" class="com.kk.pojo.User" p:name="王五" p:age="22"/>
<!-- c 命名空间注入 需要构造器 construct-args -->
<!-- singleton 单例模式 Spring的默认机制-->
<!-- prototype原型模式 每次从容器中get的时候,每一次都会产生一个新对象 -->
<bean id="user2" class="com.kk.pojo.User" c:name="赵六" c:age="20" scope="prototype"/>
</beans>
注意:需要导入以下xmlns
xmlns:p="http://www.springframework.org/schema/p
xmlns:c="http://www.springframework.org/schema/c
p命名空间
p 命名空间注入,可以直接注入属性的值 property
c命名空间
c 命名空间注入 需要构造器 construct-args
singleton 单例模式 Spring的默认机制
prototype原型模式 每次从容器中get的时候,每一次都会产生一个新对象
测试各种类型的注入
仔细阅读注解
beans.xml 用于将实体类Address,Student注入到spring容器中,交给spring托管
<?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.0.xsd">
<bean id="address" class="com.kk.pojo.Address">
<property name="address" value="广东"/>
</bean>
<bean id="student" class="com.kk.pojo.Student">
<!-- 第一种:普通值注入,使用value注入-->
<property name="name" value="小六"/>
<!-- 第二种:bean注入,使用ref,其中name里边的参数是写bean中的id参数-->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>Java程序设计</value>
<value>SSM</value>
<value>微服务</value>
</array>
</property>
<!-- List注入-->
<property name="hobbys">
<list>
<value>敲代码</value>
<value>赚钱</value>
<value>唱歌</value>
<value>运动</value>
</list>
</property>
<!-- map注入-->
<property name="card">
<map>
<entry key="学号" value="2011101**"/>
<entry key="身份证" value="4418 **** **** ****..."/>
</map>
</property>
<!-- set注入-->
<property name="games">
<set>
<value>CSOL</value>
<value>王者</value>
</set>
</property>
<!-- 空 null 注入-->
<property name="wife">
<null/>
</property>
<!-- Properties注入-->
<property name="info">
<props>
<prop key="邮箱">[email protected]</prop>
<prop key="qq">30666</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
property中的name里边的参数对应的是实体类中的属性字段
测试:
public class Test {
@org.junit.Test
public void testAddress(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Address address = (Address) context.getBean("address");
System.out.println(address);
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
@org.junit.Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
User user = context.getBean("user", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user==user2);//结果为 false 这就是单例模式 singleton
}
}
04、Autowired:自动装配
要使用注解,首先必须开启注解支持,再beans.xml中添加以下字段
<!-- 开启注解的支持-->
<context:annotation-config/>
编写三个实体类
Cat
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
Dog
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
People
package com.kk.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("all")
public class People {
//@Resource也可以
/**
* @Autowired 通过byName方式实现,必须要求这个对象存在
* @Resource 默认通过byName方式实现,如果找不到名字,则通过byType方式实现
* @Qualifier 给这个注入的类指定特定的名称
* 执行顺序不同
*/
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解的支持-->
<context:annotation-config/>
<bean id="cat111" class="com.kk.pojo.Cat"/>
<bean id="dog" class="com.kk.pojo.Dog"/>
<!-- autowire 自动装配
byName :会自动在容器上下文中查找和自己对象set方法后面的值 对应的beanid
byType:会自动在容器上下文中查找和自己对象属性类型(class)(com.kk.pojo.Cat)相同的bean 必须保证类型全局唯一
-->
<!-- <bean id="people" class="com.kk.pojo.People" autowire="byType">-->
<!-- <property name="name" value="赵六"/>-->
<!-- <property name="dog" ref="dog"/>-->
<!-- <property name="cat" ref="cat111"/>-->
<!-- </bean>-->
<bean id="people" class="com.kk.pojo.People">
<!-- 给People类中的属性name赋值 qx-->
<property name="name" value="qx"/>
</bean>
</beans>
测试:
public class Test {
@org.junit.Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
System.out.println(people.getName());
people.getDog().shout();
people.getCat().shout();
}
}
当我们没有使用@Autowired注解的时候,我们使用传统的方法
<bean id="cat111" class="com.kk.pojo.Cat"/>
<bean id="dog" class="com.kk.pojo.Dog"/>
<bean id="people" class="com.kk.pojo.People" autowire="byType">
<property name="name" value="赵六"/>
<property name="dog" ref="dog"/>
<property name="cat" ref="cat111"/>
</bean>
autowire里边的参数有两个选项:
byName :会自动在容器上下文中查找和自己对象set方法后面的值,即对应的bean中的id
byType :会自动在容器上下文中查找和自己对象属性类型(class)(com.kk.pojo.Cat)相同的bean 必须保证类型全局唯一
当我们使用@Autowired注解
我们需要在people类中加上以下注解,其中@Qualifier是标注这个类在spring容器中的名字,需要一一对应
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
<!-- 开启注解的支持-->
<context:annotation-config/>
<bean id="cat111" class="com.kk.pojo.Cat"/>
<bean id="dog" class="com.kk.pojo.Dog"/>
<bean id="people" class="com.kk.pojo.People"/>
总结:Autowired的作用是什么?
Autowired自动装配:在People实体类中,如果我们想要调用Cat以及Dog的方法,只需将他们的类编写进来,然后在方法上加上@Autowired注解,并且在beans.xml中将它们注入到spring容器中,交给spring托管,这时我们就可以使用实体类People去调用Cat和Dog里边的方法。
相当于我们在实体类中编写实体类的方法并且使用set
@Qualifier:给bean声明一个特有的名字
05、注解:Annotation
首先不惜在spring容器配置中加上以下字段:
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 指定要扫描的包-->
<context:component-scan base-package="com.kk"/>
<!-- 开启注解的支持-->
<context:annotation-config/>
</beans>
<context:component-scan base-package="com.kk"/> 扫面com.kk目录下所有的注解
实体类:User
@Component注解的作用
在实体类中加上@Component注解,相当于在applicationContext.xml中添加 <bean id="user" class="com.kk.pojo.User"/>
其中在实体类中的字段加上值可以使用 @Value(""),相当于 <property name="name" value="赵六"/>
<bean id="user" class="com.kk.pojo.User"/>
<property name="name" value="赵六"/>
</bean>
//@Component 等价 于 <bean id="user" class="com.kk.pojo.User"/>
//其中id为@Component里边的参数user @Component("user")
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
// @Value("赵六") 等价 于 <property name="name" value="赵六"/>
@Value("赵六")
public String name;
}
dao层
@Repository
在dao层的接口类中,如果我们想要把其中的类交给spring容器托管,我们可以使用@Repository注解
import org.springframework.stereotype.Repository;
@Repository
public interface UserDao {
}
service层
@Service
在service层的接口类中,如果我们想要把其中的类交给spring容器托管,我们可以使用@Servicea注解
@Service
public interface UserService {
}
controller层
@Controller
在controller层中,如果我们想要把其中的类交给spring容器托管,我们可以使用@Controller注解
@Controller
public class UserControl {
}
测试:
public class Test {
@org.junit.Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = context.getBean("user", User.class);
System.out.println(user.getName());
}
}
tip:
ClassPathXmlApplicationContext是spring读取xml最常用的类。而我们一般操作的是ta的接口ApplicationContext。BeanFactory和ApplicationContext区别不大,BeanFactory不在自动BeanPostProcessor和自动 BeanFactoryPostProcessor 上注册。
06、Java配置类:JavaConfig
在spring配置中 applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 指定要扫描的包-->
<context:component-scan base-package="com.kk"/>
<!-- 开启注解的支持-->
<context:annotation-config/>
</beans>
@Configuration的作用
@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
首先我在com.kk.config目录下创建一个自己的配置类 MyConfig
仔细阅读注解
-
@Configuration类似于applicationContext.xml中的beans
-
@Configuration 这个也会被Spring容器托管 注册到容器中 因为它本来就是一个@Component
-
@Configuration 代表这是一个配置类,就和之前的beans.xml一样
/**
* @Configuration类似于applicationContext.xml中的beans
* @Configuration 这个也会被Spring容器托管 注册到容器中 因为它本来就是一个@Component
* @Configuration 代表这是一个配置类,就和之前的beans.xml一样
*/
@Configuration
@ComponentScan("com.kk.pojo")
@Import(MyConfig_2.class)
public class MyConfig {
/**
* 注册一个bean,就相当于之前写的一个bean标签
* 这个方法的名字(getUser)就相当于bean标签中的id
*这个方法的返回值(User)就相当于bean标签中的class属性
*/
@Bean
public User getUser(){
return new User();//就是返回要注入到bean的对象
}
}
@Bean的作用
将这个类或者方法注册到spring IoC容器中,这个配置就等同于之前在xml里的配置
<beans>
<bean id="getUser" class="com.kk.pojo.User"/>
</beans>
-
注册一个bean,就相当于之前写的一个bean标签
-
这个方法的名字(getUser)就相当于bean标签中的id
-
这个方法的返回值(User)就相当于bean标签中的class属性
实际上是通过反射、代理来实现的
@componentscan的作用
@ComponentScan告诉Spring 哪个packages 用注解标识的类 ,会被spring自动扫描并且装入bean容器。
例如,如果你有个类用@Controller注解标识了,那么,如果不加上@ComponentScan,自动扫描该controller,那么该Controller就不会被spring扫描到,更不会装入spring容器中,因此你配置的这个Controller也没有意义。
@Import的作用
传入其他自定义配置类
@Configuration
public class MyConfig_2 {
}
测试:如果完全使用了配置类去做,就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
import com.kk.config.MyConfig;
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
//如果完全使用了配置类去做,就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.getName());
}
}
@PropertySource(“db.properties”)
作用:用于在Java配置文件中读取resources目录下的db.properties文件
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
user=root
password=123456
MyConfig.java
package com.kk.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.kk.pojo.User;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
@SuppressWarnings("all")
@Configuration
@PropertySource("db.properties")
public class MyConfig {
@Value("${drive}")
private String driverClassName;
@Value("${url}")
private String url;
@Value("${user}")
private String username;
@Value("${password}")
private String password;
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
测试:
package com.kk;
import com.kk.config.MyConfig;
import com.kk.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class TestMyConfigDataSource {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
DataSource bean = context.getBean(DataSource.class);
System.out.println(bean);
}
}
已经读取到配置文件db.properties的数据
07、代理模式(静态、动态):Proxy_Mode
在了解aop之前我们先来了解代理模式
静态代理模式示例一
租房接口类 Rent
public interface Rent {
public void rent();
}
房东类 Host
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
中介类 代理 Proxy 可以附带自己的一些属性
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Proxy implements Rent{
private Host host;
@Override
public void rent() {
host.rent();
seeHouse();
fare();
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void fare(){
System.out.println("收中介费");
}
}
租客类 Client
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
//代理,中介帮房东租房子, 代理角色一般会有一些附属操作
proxy.rent();
}
}
静态代理模式示例二
service接口类 UserService
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
service接口实现类
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
代理 service实现类 UserServiceProxy
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
//打印日志方法
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}
用户执行
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
System.out.println("==========");
proxy.delete();
System.out.println("==========");
proxy.update();
System.out.println("==========");
proxy.query();
}
}
总结:当我们的用户想要去调用service实现类的方法的时候,这时并不直接从service实现类中取得某个方法,而是通过接口去取
注意点:需要在代理实现类 UserServiceProxy 中编写以下代码
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
当我们的用户 Client 在调用的使用只需编写以下代码
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
System.out.println("==========");
proxy.delete();
System.out.println("==========");
proxy.update();
System.out.println("==========");
proxy.query();
动态代理模式实例三
租房 接口 Rent
public interface Rent {
public void rent();
}
房东 Host 实现接口
public class Host implements Rent {
@Override
public void rent() {
System.out.println("房东出租房子");
}
}
代理类 Proxy_InvocationHandler 实现 InvocationHandler接口
InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
package com.kk.Demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Proxy_InvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
/**
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
*
*/
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this );
}
//处理代理实例并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
fare();
/**
* public Object invoke(Object obj, Object... args)
*/
//动态代理的本质就是实现反射机制
Object result = method.invoke(rent, args);
return result;
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void fare(){
System.out.println("收中介费");
}
}
用户
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色
Proxy_InvocationHandler handler = new Proxy_InvocationHandler();
//通过调用 程序 处理 角色 来处理 要调用 的接口对象
handler.setRent(host);
Rent proxy = (Rent) handler.getProxy();//这里的proxy就是动态生成的,我们并没有写
proxy.rent();
}
}
动态代理模式实例四
Proxy_InvocationHandler
package com.kk.Demo04;
import com.kk.Demo03.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@SuppressWarnings("all")
public class Proxy_InvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this );
}
//处理代理实例并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//动态代理的本质就是实现反射机制
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
Client
package com.kk.Demo04;
import com.kk.Demo02.UserService;
import com.kk.Demo02.UserServiceImpl;
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色,不存在
Proxy_InvocationHandler handler = new Proxy_InvocationHandler();
//设置要代理的对象
handler.setTarget(userService);
//动态生成代理类
UserService proxy = (UserService) handler.getProxy();
proxy.add();
}
}
08、AOP
导包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.16</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
</dependencies>
AOP核心概念
- Joinpoint(连接点):所谓连接点是指那些可以被增强到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):所谓切入点是指被增强的连接点(方法)
Advice(通知/ 增强):所谓通知是指具体增强的代码
- Target(目标对象):被增强的对象就是目标对象
Aspect(切面):是切入点和通知(引介)的结合
Proxy (代理):一个类被 AOP 增强后,就产生一个结果代理类
切入点、通知以及切面
目标对象
AOP:切点表达式
可以使用切点表达式来表示要对哪些方法进行增强。
写法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略,大部分情况下省略
- 返回值类型、包名、类名、方法名可以使用星号* 代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
- 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
例如:
execution(* com.sangeng.service.*.*(..)) 表示com.sangeng.service包下任意类,方法名任意,参数列表任意,返回值类型任意
execution(* com.sangeng.service..*.*(..)) 表示com.sangeng.service包及其子包下任意类,方法名任意,参数列表任意,返回值类型任意
execution(* com.sangeng.service.*.*()) 表示com.sangeng.service包下任意类,方法名任意,要求方法不能有参数,返回值类型任意
execution(* com.sangeng.service.*.delete*(..)) 表示com.sangeng.service包下任意类,要求方法不能有参数,返回值类型任意,方法名要求已delete开头
在resouces目录下创建applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 指定要扫描的包-->
<context:component-scan base-package="com.kk"/>
<!-- 开启注解的支持-->
<context:annotation-config/>
<!-- 开启aop注解支持-->
<aop:aspectj-autoproxy/>
</beans>
创建MyAspect.java类
package com.kk.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author :
* @Date : 2022/4/9
* @Desc :
*/
@Component
@Aspect
public class MyAspect {
// @Pointcut("execution(* com.kk.service.*.*(..))")
// public void pt(){
//
// }
// @Before("pt()")
// public void methodbefore(){
// System.out.println("方法被调用了");
// }
//需要无参才生效
// @Pointcut("execution(* com.kk.service..*.*())")
// public void ptt(){
//
// }
@Pointcut("execution(* com.kk.service.*.delete*(..))")
public void ptt(){
}
@Before("ptt()")
public void methodbeforeppt(){
System.out.println("方法被调用了");
}
}
创建service层,在service层创建UserService和PhoneService
UserService
package com.kk.service;
import com.kk.aspect.InvokeLog;
import org.springframework.stereotype.Service;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
@Service
public class UserService {
@InvokeLog
public void qx(){
System.out.println("UserService中qx的核心代码");
}
public void deleteAll(){
System.out.println("以delete开头的");
}
}
PhoneService
package com.kk.service;
import com.kk.aspect.InvokeLog;
import org.springframework.stereotype.Service;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
@Service
public class PhoneService {
@InvokeLog
public void qx(){
System.out.println("PhoneService中qx的核心代码");
}
}
测试:
package com.kk;
import com.kk.service.PhoneService;
import com.kk.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class Demo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PhoneService phoneService = context.getBean(PhoneService.class);
UserService userService = context.getBean(UserService.class);
phoneService.qx();
userService.qx();
userService.deleteAll();
}
}
AOP:使用切点表达式@annotation
我们也可以在要增强的方法上加上注解。然后使用@annotation来表示对加了什么注解的方法进行增强。
写法:@annotation(注解的全类名)
例如:
创建一个注解类InvokeLog
package com.kk.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author : k
* @Date : 2022/4/10
* @Desc :
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.METHOD})
public @interface InvokeLog {
}
在MyAspect类中使用此注解类
@Pointcut("@annotation(com.kk.aspect.InvokeLog)")
package com.kk.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @author :
* @Date : 2022/4/9
* @Desc :
*/
@Component
@Aspect
public class MyAspect {
@Pointcut("@annotation(com.kk.aspect.InvokeLog)")
public void ptt(){
}
@Before("ptt()")
public void methodbeforeppt(){
System.out.println("方法被调用了");
}
}
UserSerice以及PhoneService
若哪个类的哪个方法需要使用aop,则在方法上面加 @InvokeLog 即可
UserSerice
package com.kk.service;
import com.kk.aspect.InvokeLog;
import org.springframework.stereotype.Service;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
@Service
public class UserService {
@InvokeLog
public void qx(){
System.out.println("UserService中qx的核心代码");
}
public void deleteAll(){
System.out.println("以delete开头的");
}
}
PhoneService
package com.kk.service;
import com.kk.aspect.InvokeLog;
import org.springframework.stereotype.Service;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
@Service
public class PhoneService {
@InvokeLog
public void qx(){
System.out.println("PhoneService中qx的核心代码");
}
}
测试:
package com.kk;
import com.kk.service.PhoneService;
import com.kk.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : k
* @Date : 2022/4/9
* @Desc :
*/
public class Demo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
PhoneService phoneService = context.getBean(PhoneService.class);
UserService userService = context.getBean(UserService.class);
phoneService.qx();
userService.qx();
userService.deleteAll();
}
}
通知分类
- @Before:前置通知,在目标方法执行前执行
- @AfterReturning: 返回后通知,在目标方法执行后执行,如果出现异常不会执行
- @After:后置通知,在目标方法之后执行,无论是否出现异常都会执行
- @AfterThrowing:异常通知,在目标方法抛出异常后执行
- @Around:环绕通知,围绕着目标方法执行
其他代码不变,只需修改MyAspect中的代码即可
package com.kk.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author :
* @Date : 2022/4/9
* @Desc :
*/
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* com.kk.service..*.*(..))")
public void ptt(){
}
@Before("ptt()")
public void methodbeforeppt(){
System.out.println("before");
}
@AfterReturning("ptt()")
public void afterReturning(){
System.out.println("afterReturning");
}
@After("ptt()")
public void after(){
System.out.println("after");
}
@AfterThrowing("ptt()")
public void afterThrowing(){
System.out.println("after");
}
//环绕通知非常特殊,它可以对目标方法进行全方位的增强。
@Around("ptt()")
public void around(ProceedingJoinPoint pjp){
System.out.println("around目标方法前");
try {
pjp.proceed();//目标方法执行
System.out.println("around目标方法后");
} catch (Throwable e) {
e.printStackTrace();
}finally {
System.out.println("finally中进行增强");
}
}
}
获取被增强方法相关信息
我们实际对方法进行增强时往往还需要获取到被增强代码的相关信息,比如方法名,参数,返回值,异常对象等。
我们可以在除了环绕通知外的所有通知方法中增加一个JoinPoint类型的参数。这个参数封装了被增强方法的相关信息。我们可以通过这个参数获取到除了异常对象和返回值之外的所有信息。
例如:
@Before("pt()")
public void methodbefore(JoinPoint jp){
Object[] args = jp.getArgs();//方法调用时传入的参数
Object target = jp.getTarget();//被代理对象
MethodSignature signature = (MethodSignature) jp.getSignature();//获取被被增强方法签名封装的对象
System.out.println("Before方法被调用了");
}
案例:
需求:要求让所有service包下类的所有方法被调用前都输出全类名,方法名,以及调用时传入的参数
package com.kk.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author : k
* @Date : 2022/4/13
* @Desc :
*/
@Component
@Aspect
public class PrintLogAspect {
//对哪些方法增强
@Pointcut("execution(* com.kk.service..*.*(..))")
public void pt(){
}
//怎么增强
@Before("pt()")
public void printLog(JoinPoint joinPoint){
//输出 被调用的方法所在的类名 方法名 调用时传入的参数
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String classMethod = joinPoint.getSignature().getDeclaringTypeName();//类名 com.kk.service.UserService
String methodName = signature.getName();//方法名 updateById
Object[] args = joinPoint.getArgs();//调用时传入的参数 556688
System.out.println(classMethod+"=="+methodName+"=="+Arrays.toString(args));
}
}
果需要获取被增强方法中的异常对象或者返回值
则需要在方法参数上增加一个对应类型的参数,并且使用注解的属性进行配置。这样Spring会把你想获取的数据赋值给对应的方法参数。
例如:
package com.kk.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author :
* @Date : 2022/4/9
* @Desc :
*/
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* com.kk.service..*.*(..))")
public void ptt(){
}
@Before("ptt()")
public void methodbeforeppt(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println(method);//public void com.kk.service.UserService.updateById(java.lang.Integer)
// method.invoke();
System.out.println("before");
}
@AfterReturning(value = "ptt()", returning = "ret")
public void afterReturning(JoinPoint joinPoint,Object ret){
System.out.println("afterReturning");
}
@After("ptt()")
public void after(JoinPoint joinPoint){
System.out.println("after");
}
@AfterThrowing(value = "ptt()", throwing ="t")
public void afterThrowing(JoinPoint joinPoint,Throwable t){
System.out.println("after");
}
}
相信你肯定觉得上面的获取方式特别的麻烦难以理解。就可以使用下面这种万能的方法。
直接在环绕通知方法中增加一个ProceedingJoinPoint类型的参数。这个参数封装了被增强方法的相关信息。
该参数的proceed()方法被调用相当于被增强方法被执行,调用后的返回值就相当于被增强方法的返回值。
例如:
package com.kk.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @author :
* @Date : 2022/4/9
* @Desc :
*/
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* com.kk.service..*.*(..))")
public void ptt(){
}
@Around("ptt()")
public void around(ProceedingJoinPoint pjp) {
Object[] args = pjp.getArgs(); //获取方法参数
MethodSignature signature = (MethodSignature) pjp.getSignature();
Object target = pjp.getTarget(); //获取被增强的对象
try {
Object ret = pjp.proceed();//目标方法的执行
//ret就是被增强方法的返回值
System.out.println(ret);
} catch (Throwable e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println(pjp);
}
}
【不使用自动注入】AOP方式一:使用配置文件方法
功能:要求代码在执行的时候添加日志并且打印出日志信息,当不改变业务的源代码
编写两个日志类
Log 实现MethodBeforeAdvice
public class Log implements MethodBeforeAdvice {
/**
* void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
* @param method 要执行的目标对象的方法,获取实现类的名字 target.getClass().getName() ,获取方法的名字method.getName()
* @param args 参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
After_Log 实现 AfterReturningAdvice
public class After_Log implements AfterReturningAdvice {
/**
*
* @param returnValue 返回值
* @param method 获取方法的名字method.getName()
* @param args
* @param target
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
applicationContext.xml
将实现类以及两个自定义的log类注入到spring IoC容器中,由spring托管
<!-- 注册bean-->
<bean id="userService" class="com.kk.service.UserServiceImpl"/>
<bean id="log" class="com.kk.log.Log"/>
<bean id="afterLog" class="com.kk.log.After_Log"/>
注意点:需要导入aop的约束:
xmlns:aop=“http://www.springframework.org/schema/aop”
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id="userService" class="com.kk.service.UserServiceImpl"/>
<bean id="log" class="com.kk.log.Log"/>
<bean id="afterLog" class="com.kk.log.After_Log"/>
<!-- 方式一:使用原生的Spring API接口-->
<aop:config>
<!-- 切入点 expression表达式 execution 要执行的位置 就是这个切入点要在哪里执行 -->
<aop:pointcut id="pointcut" expression="execution(* com.kk.service.UserServiceImpl.*(..))"/>
<!-- 这句话的意思就是 我们把这个log类 ( <bean id="log" class="com.kk.log.Log"/>) 切入到这个pointcut 方法里边-->
<!-- advice-ref引用哪一个类进行切入 pointcut-ref将这个类切入到哪里-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
【不使用自动注入】AOP方式二:自定义切点
自定义类 DiyPointCut
public class DiyPointCut {
public void before(){
System.out.println("=======方法执行前======");
}
public void after(){
System.out.println("=======方法执行后======");
}
}
applicationContext.xml
将自定义类 DiyPointCut注入到spring IoC容器中,由spring托管
<!-- 方式二:自定义类-->
<bean id="diy" class="com.kk.diy.DiyPointCut"/>
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id="userService" class="com.kk.service.UserServiceImpl"/>
<!-- 方式二:自定义类-->
<bean id="diy" class="com.kk.diy.DiyPointCut"/>
<aop:config>
<!-- 自定义切面 ref 要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点,就是在哪个地方使用aop,就是这个切入点要在哪里执行-->
<aop:pointcut id="point" expression="execution(* com.kk.service.UserServiceImpl.*(..))"/>
<!-- 通知 method 就是要使用的方法(自定义类 DiyPointCut里边的方法) pointcut-ref这个方法要在哪里执行-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
【不使用自动注入】AOP方式三:使用注解方法
通过注解编写切面
注意:需要使用@Aspect这个注解标注这个类是一个切面
自定义类 AnnotationPointCut
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.kk.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=======方法执行前======");
}
@After("execution(* com.kk.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=======方法执行后======");
}
//在环绕增强中,我们可以给定一个参数 代表我们要处理切入的点
@Around("execution(* com.kk.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
applicationContext.xml
注意:需要开启注解支持
<!--开启注解支持 -->
<aop:aspectj-autoproxy/>
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id="userService" class="com.kk.service.UserServiceImpl"/>
<!-- 方式三:-->
<bean id="annotationPointCut" class="com.kk.diy.AnnotationPointCut"/>
<!--开启注解支持 -->
<aop:aspectj-autoproxy/>
</beans>
09、Spring整合Mybatis
导包:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.16</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
</dependencies>
资源导出问题:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
1.配置数据源文件:在resources目录下创建db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
password=123456
2.编写mybatis-config.xml文件
用于mybatis的配置文件
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
<!-- 重点:引入外部配置文件-->
<properties resource="db.properties"/>
<settings>
<!-- 自带的日志文件-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 开启驼峰命名转换-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
<!-- 设置sql超时时间-->
<setting name="defaultStatementTimeout" value="3" />
</settings>
<!-- 给实体类起别名别名-->
<typeAliases>
<typeAlias type="com.kk.pojo.User" alias="User"/>
</typeAliases>
</configuration>
3.编写spring-dao.xml配置文件
用于spring配置文件,并且连接mybatis的配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 读取 db.properties 配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- DataSource 使用Spring的数据源替换Mybatis的配置
我们这里使用Spring提供的JDBC org.springframework.jdbc.datasource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="123456"/>-->
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kk/mapper/*.xml"/>
</bean>
<!-- SqlSessionTemplate 就是我们使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory,因为ta没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
4.将mybati以及spring整合在一个ApplicationContext.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring_dao.xml"/>
<bean id="userMapper" class="com.kk.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
实体类 User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
}
mapper层
UserMapper
接口
public interface UserMapper {
public List<User> selectUser();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kk.mapper.UserMapper">
<select id="selectUser" resultType="User">
select * from mybatis.user ;
</select>
</mapper>
接口实现类 UserMapperImpl
public class UserMapperImpl implements UserMapper{
//我们的所有操作,,在原来都使用SqlSession来执行,现在都使用SqlSessionTemple;
private SqlSessionTemplate sqlSession;
//需要将它注入进来
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
return userList;
}
}
拓展:使用实现类继承SqlSessionDaoSupport
UserMapperImpl2
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
return userList;
}
}
在spring_dao.xml中加入以下字段
<!-- 整合mybatis方式2-->
<bean id="userMapper2" class="com.kk.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
10、Transaction
当我们在编写数据增删改的时候,如果没有设置提交事务,即使运行成功,它的数据还是不会提交到数据库中,这时我们如何在不改变业务层源代码的情况下对事务进行提交呢? AOP 切面编程
spring整合mybatis的配置文件09有,这里不再编写
但是我们需要增加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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- DataSource 使用Spring的数据源替换Mybatis的配置
我们这里使用Spring提供的JDBC org.springframework.jdbc.datasource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kk/mapper/*.xml"/>
</bean>
<!-- SqlSessionTemplate 就是我们使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory,因为ta没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!-- ===============================================================-->
<!-- 配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合aop 实现事务的注入-->
<!-- 配置事务 通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务-->
<!-- 配置事务的传播特性 propagation-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入-->
<aop:config>
<!-- 切入点 谁需要事务-->
<aop:pointcut id="txPointCut" expression="execution(* com.kk.mapper.*.*(..))"/>
<!-- 切入 将事务切入到哪里 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>