IOC(控制反转)
程序中不要主动用new产生对象,对象的创建控制权转移到外部,这就是所谓的控制反转。
比如没有spring ioc之前我们是这样写的:
package com.akbar.ioc;
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl(); // 这种手动new耦合度很高
@Override
public void save() {
bookDao.save();
System.out.println("bookService save ....");
}
}
使用spring ioc思想解决这个问题(但是下面的代码还不能运行,只是举个例子):
不用自己主动new对象,而从外部提供对象(spring ioc容器)
package com.akbar.ioc;
public class BookServiceImpl implements BookService {
private BookDao bookDao; // 耦合度低了
@Override
public void save() {
bookDao.save();
System.out.println("bookService save ....");
}
}
由spring ioc容器管理的对象称为Bean。
IOC配置
在pom.xml中导入spring的坐标:
<?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.akbar</groupId>
<artifactId>ssm01</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.8</version>
</dependency>
</dependencies>
</project>
在resources目录下创建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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置Bean-->
<!--注意我们交给ioc容器的是实现类,不是接口,因为后续需要早对象-->
<!--
class: 要管理的Bean
id: 给这个Bean起个名字,可以随便起
-->
<bean id="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl"/>
</beans>
看看我的BookServiceImpl:
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.dao.impl.BookDaoImpl;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
@Override
public void save() {
bookDao.save();
System.out.println("BookService save...");
}
}
注意,我们还没进行依赖注入,所以如果不写
new BookDaoImpl(),程序会报nullPointerException。这是因为spring不会自动给对象内部的属性注入依赖。
然后在启动类中获取IOC容器并获取Bean,然后执行:
package com.akbar;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 获取IOC容器
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取Bean
BookDao bookDao = (BookDao) app.getBean("bookDao");
BookService bookService = (BookService) app.getBean("bookService");
bookService.save();
}
}
DI 依赖注入入门
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.xsd">
<!--配置Bean-->
<!--注意我们交给ioc容器的是实现类,不是接口,因为后续需要早对象-->
<!--
class: 要管理的Bean
id: 给这个Bean起个名字,可以随便起
-->
<bean id="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<!--配置service和dao的关系-->
<!-- 因为service里面放dao对象,所以要在service中配置 -->
<!-- name="bookDao"这里的bookDao是service中的成员变量bookDao -->
<!-- ref="bookDao"这里的bookDao是id="bookDao"的Bean -->
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
然后我们可以把BookServiceImpl中的new去掉了
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
// 不用通过new造对象了
private BookDao bookDao;
// 提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
bookDao.save();
System.out.println("BookService save...");
}
}
配置Bean别名
可能你的命名习惯和我的命名习惯不一样,这时候我们可以给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="bookDao" name="dao" class="com.akbar.dao.impl.BookDaoImpl"/>
<!-- 用name属性起别名,不同的别名之间可以使用逗号,空格和分号隔开。 -->
<!--property中的ref也可以填上面的Bean的name-->
<bean id="bookService" name="service myService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="dao"/>
</bean>
</beans>
使用别名获取Bean
package com.akbar;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 获取IOC容器
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取Bean
BookDao bookDao = (BookDao) app.getBean("bookDao");
//bookService service myService,可以使用这三个名称获取这个Bean
BookService bookService = (BookService) app.getBean("myService");
bookService.save();
}
}
Bean的作用范围控制
package com.akbar;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 获取IOC容器
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService1 = (BookService) app.getBean("bookService");
BookService bookService2 = (BookService) app.getBean("bookService");
// 默认情况下这两个Bean的内存地址是一样的,就是单例的
System.out.println(bookService1);
System.out.println(bookService2);
}
}
我们可以在配置文件中进行这个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="bookDao" name="dao" class="com.akbar.dao.impl.BookDaoImpl"/>
<!-- scope默认是singleton(单例的),我们可以设置成scope="prototype"多例的 -->
<bean id="bookService" name="service" class="com.akbar.service.impl.BookServiceImpl" scope="prototype">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
这样以后我们打印的Bean的地址就是不一样的。
Bean的实例化
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="bookService" name="service" class="com.akbar.service.impl.BookServiceImpl"/>
</beans>
package com.akbar.service.impl;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
private BookServiceImpl() {
System.out.println("BookServiceImpl constructor is running...");
}
@Override
public void save() {
System.out.println("BookService save...");
}
}
package com.akbar;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 获取IOC容器
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) app.getBean("bookService");
bookService.save();
}
}
输出结果中可以看到,
BookServiceImpl的构造方法也执行了。
spring造Bean的时候调的是无参的构造方法。
静态工厂方法造Bean
先写一下代码结构:
package com.akbar.dao;
public interface BookDao {
void save();
}
package com.akbar.dao.impl;
import com.akbar.dao.BookDao;
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDao save...");
}
}
package com.akbar.dao.factory;
import com.akbar.dao.BookDao;
import com.akbar.dao.impl.BookDaoImpl;
public class BookDaoFactory {
public static BookDao getBookDao() {
return new BookDaoImpl();
}
}
在App中运行:
package com.akbar;
import com.akbar.dao.BookDao;
import com.akbar.dao.factory.BookDaoFactory;
public class App {
public static void main(String[] args) {
BookDao bookDao = BookDaoFactory.getBookDao();
bookDao.save();
}
}
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.xsd">
<!--这个造出来的是BookDaoFactory的对象,我们不需要这个BookDaoFactory的对象-->
<!--<bean id="bookDao" class="com.akbar.factory.BookDaoFactory" />-->
<!--这个造出来的是BookDaoImpl的对象,别忘了指定factory-method="getBookDao"-->
<bean id="bookDao" class="com.akbar.factory.BookDaoFactory" factory-method="getBookDao" />
</beans>
在 App中运行
package com.akbar;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) app.getBean("bookDao");
bookDao.save();
}
}
用实例工厂造对象
注意,这里的工厂方法不是静态的
package com.akbar.factory;
import com.akbar.dao.BookDao;
import com.akbar.dao.impl.BookDaoImpl;
public class BookDaoFactory {
public BookDao getBookDao() { // 注意不是静态方法
System.out.println("factory setup ...");
return new BookDaoImpl();
}
}
配置文件:
<?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-->
<bean id="bookDaoFactory" class="com.akbar.factory.BookDaoFactory"/>
<!--注意这里不用指定class,factory-bean要写上面BookDaoFactory的Bean-->
<bean id="bookDao" factory-bean="bookDaoFactory" factory-method="getBookDao"/>
</beans>
然后在App里面运行:
package com.akbar;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) app.getBean("bookDao");
bookDao.save();
}
}
改良:
我们发现下面的配置纯粹是为了配置而配置,我们无缘无故的造了一个bookDaoFactory的Bean,有没有办法改良呢?
<!--先造工厂的Bean-->
<bean id="bookDaoFactory" class="com.akbar.factory.BookDaoFactory"/>
我们先创建bookDaoFactoryBean,让这个类实现spring的FactoryBean<BookDao>。
package com.akbar.factory;
import com.akbar.dao.BookDao;
import com.akbar.dao.impl.BookDaoImpl;
import org.springframework.beans.factory.FactoryBean;
public class BookDaoFactoryBean implements FactoryBean<BookDao> {
// 代替原来实例工厂中创建对象的方法
@Override
public BookDao getObject() throws Exception {
return new BookDaoImpl();
}
// 指定什么类型的Bean
@Override
public Class<?> getObjectType() {
return BookDao.class;
}
// 指定造出来的Bean单例的还是非单例的
@Override
public boolean isSingleton() {
return true; // true表示单例的
}
}
然后配置文件怎么写呢?
<?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">
<!--使用factoryBean实例化-->
<bean id="bookDao" class="com.akbar.factory.BookDaoFactoryBean" />
</beans>
在App中运行:
package com.akbar;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) app.getBean("bookDao");
bookDao.save();
}
}
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">
<!--这里用init-method和destroy-method分别制定初始化和销毁方法-->
<bean id="bookDao" class="com.akbar.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
BookDaoImpl:
package com.akbar.dao.impl;
import com.akbar.dao.BookDao;
public class BookDaoImpl implements BookDao {
@Override
public void save() {
System.out.println("BookDao save...");
}
// 表示Bean初始化对应的操作
public void init() {
System.out.println("BookDao init...");
}
// 表示Bean初始化前对应的操作
public void destroy() {
System.out.println("BookDao destroy...");
}
}
BookServiceImpl:
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
bookDao.save();
}
}
App:
package com.akbar;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 注意这里左边用的是ClassPathXmlApplicationContext,不是ApplicationContext
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) app.getBean("bookService");
bookService.save();
// 手动关闭虚拟机,这样Bean的销毁就有机会机会
app.close();
}
}
但是上面的app.close有点暴力,所以不推荐用这个。
更推荐使用app.registerShutdownHook();钩子。
package com.akbar;
import com.akbar.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 注意这里左边用的是ClassPathXmlApplicationContext,不是ApplicationContext
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) app.getBean("bookService");
bookService.save();
// 手动关闭虚拟机,这样Bean的销毁就有机会机会
app.registerShutdownHook();
}
}
更规范的写法:
更规范的写法是让BookDaoImpl除了实现自己的接口BookDao,还实现InitializingBean和DisposableBean:
package com.akbar.dao.impl;
import com.akbar.dao.BookDao;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BookDaoImpl implements BookDao, InitializingBean, DisposableBean {
@Override
public void save() {
System.out.println("BookDao save...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("BookDao init...");
}
@Override
public void destroy() throws Exception {
System.out.println("BookDao destroy...");
}
}
别忘了去掉配置文件中的init-method和destroy-method:
<?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="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
依赖注入方式
依赖注入描述了容器中建立Bean与Bean之间依赖关系的过程,如果Bean运行需要的是数字或字符串呢?
setter方法注入
setter方法注入就是提供对应的setter方法:
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.dao.UserDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
BookDao bookDao;
UserDao userDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
bookDao.save();
userDao.insert();
}
}
然后在配置文件中用ref指定要引用的对象,用name指定对应的成员变量名,比如下面配置中的name配置的是BookServiceImpl中的bookDao:
<?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="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl" />
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
Java自带的类型怎么注入呢,比如int,string等:
package com.akbar.dao.impl;
import com.akbar.dao.UserDao;
public class UserDaoImpl implements UserDao {
// 注入基本类型
private int connectionNumber;
private String connectionName;
public void setConnectionNumber(int connectionNumber) {
this.connectionNumber = connectionNumber;
}
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}
@Override
public void insert() {
System.out.println("UserDao insert..., connectionNumber:" + connectionNumber + ",connectionName:" + connectionName);
}
}
配置文件
<?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="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<!--配置基本类型-->
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl">
<property name="connectionNumber" value="10"/>
<property name="connectionName" value="Mysql"/>
</bean>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
构造器注入
很简单,只是把setter方法改成了构造器而已:
BookServiceImpl:
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.dao.UserDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
BookDao bookDao;
UserDao userDao;
// 构造器注入
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
@Override
public void save() {
bookDao.save();
userDao.insert();
}
}
UserDaoImpl:
package com.akbar.dao.impl;
import com.akbar.dao.UserDao;
public class UserDaoImpl implements UserDao {
// 注入基本类型
private int connectionNumber;
private String connectionName;
// 构造器注入
public UserDaoImpl(int connectionNumber, String connectionName) {
this.connectionNumber = connectionNumber;
this.connectionName = connectionName;
}
@Override
public void insert() {
System.out.println("UserDao insert..., connectionNumber:" + connectionNumber + ",connectionName:" + connectionName);
}
}
然后是配置文件:
<?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="bookDao" class="com.akbar.dao.impl.BookDaoImpl"/>
<!--配置基本类型-->
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
<constructor-arg name="connectionName" value="Mysql"/>
</bean>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
注意,constructor-arg中的name属性对应的是构造方法中的形参
1.解决构造方法形参名称耦合的问题:
<!--配置基本类型-->
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
<constructor-arg name="connectionName" value="Mysql"/>
</bean>
通过观察上面的配置代码发现,如果我们在写类的构造方法的时候万一写错了形参名称,就需要同步改动配置文件,有没有办法解决这个问题?
有,我们配置文件中不指定形参名,而指定参数类型(使用type属性):
<!--配置基本类型-->
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="Mysql"/>
</bean>
2. 如果有多个相同类型的变量怎么办?
我们可以指定形参的位置(使用index属性)来解决这个问题:
<!--配置基本类型-->
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="Mysql"/>
</bean>
不同的依赖注入方法选择建议
而且构造器注入有一定的耦合度,不太推荐。
依赖自动装配
什么是依赖自动装配:
1.按类型装配:
package com.akbar.service.impl;
import com.akbar.dao.UserDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
UserDao userDao;
// setter方法注入,自动装配这个一定要提供的
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.insert();
}
}
配置文件中我们不用写<propertiers/>标签了:
<?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="userDao" class="com.akbar.dao.impl.UserDaoImpl"/>
<!--按类型装配byType,会找UserDao类型的Bean,然后把对应的对象依赖注入进去-->
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
Spring 会执行以下步骤:
1️⃣ 获取
BookServiceImpl中所有需要注入的属性(通过 setter 方法分析)。
2️⃣ 识别setUserDao(UserDao userDao)的参数类型是UserDao(接口)。
3️⃣ 在 IOC 容器中查找与UserDao类型匹配的 Bean。
4️⃣ 如果有且仅有一个 Bean 的类型与UserDao类型兼容(包括其实现类),Spring 会将该 Bean 注入到userDao属性中。
容器中必须有且仅有一个与依赖属性类型匹配的 Bean,否则 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.xsd">
<!--有两个相同类型的Bean,spring会报错-->
<bean id="userDao1" class="com.akbar.dao.impl.UserDaoImpl"/>
<bean id="userDao2" class="com.akbar.dao.impl.UserDaoImpl"/>
<!--按类型装配byType,会找UserDao类型的Bean,然后把对应的对象依赖注入进去-->
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
2. 按名称装配:
Spring 在自动注入依赖时,会根据属性名去容器中查找与之同名的 Bean,并将该 Bean 注入到对应的属性中。
比如:
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl" autowire="byName"/>
package com.akbar.service.impl;
import com.akbar.dao.UserDao;
import com.akbar.service.BookService;
public class BookServiceImpl implements BookService {
UserDao userDao;
// setter方法注入,自动装配这个一定要提供的
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.insert();
}
}
识别
BookServiceImpl中所有需要注入的属性(通过Setter 方法)。比如上面的代码中,Spring 确定需要注入的属性名是:
userDao
完整配置代码:
<?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="userDao" class="com.akbar.dao.impl.UserDaoImpl"/>
<!--BookServiceImpl中找到属性userDao,如果上面的Bean的id="userDao"和userDao相同,就可以自动装配-->
<bean id="bookService" class="com.akbar.service.impl.BookServiceImpl" autowire="byName"/>
</beans>
依赖自动装配的特征:
集合注入
代码示例:
package com.akbar.dao.impl;
import com.akbar.dao.BookDao;
import java.util.*;
public class BookDaoImpl implements BookDao{
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
private Properties properties;
public void setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public void save() {
System.out.println("bookDao save...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历list:" + list);
System.out.println("遍历set:" + set);
System.out.println("遍历map:" + map);
System.out.println("遍历properties:" + properties);
}
}
什么是Properties类型:
Properties是继承自Hashtable<Object,Object>的类,用于以键值对(通常是字符串)的形式管理配置属性,是 Java 中用于配置文件处理的常用工具类。
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.akbar.dao.impl.BookDaoImpl">
<!--数组-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--list集合-->
<property name="list">
<list>
<value>akbar</value>
<value>saipulla</value>
<value>gulhuma</value>
</list>
</property>
<!--set集合-->
<property name="set">
<set>
<value>nihao</value>
<value>hello</value>
<value>nice</value>
<value>nice</value>
</set>
</property>
<!--map集合-->
<property name="map">
<map>
<entry key="country" value="China"/>
<entry key="province" value="Hubei"/>
<entry key="city" value="Yichang"/>
</map>
</property>
<!--Properties类型-->
<property name="properties">
<props>
<prop key="country">China</prop>
<prop key="province">Hubei</prop>
<prop key="city">Yichang</prop>
</props>
</property>
</bean>
</beans>
数据源对象管理
druid数据库连接池
坐标:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.25</version>
</dependency>
在applicationProperties.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">
<!--使用setter方法注入-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/big_event"/>
<property name="username" value="root"/>
<property name="password" value="#123456*"/>
</bean>
</beans>
在App中运行:
package com.akbar;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
// 注意这里左边用的是ClassPathXmlApplicationContext,不是ApplicationContext
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) app.getBean("dataSource");
System.out.println(dataSource);
}
}
c3p0数据库连接池
依赖坐标:
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.3.0</version>
</dependency>
在applicationProperties.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="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/big_event"/>
<property name="user" value="root"/>
<property name="password" value="#123456*"/>
</bean>
</beans>
在App中运行:
package com.akbar;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
// 注意这里左边用的是ClassPathXmlApplicationContext,不是ApplicationContext
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) app.getBean("dataSource");
System.out.println(dataSource);
}
}
加载properties文件
视频地址:
加载xml配置文件的方法
加载类路径下的配置文件
package com.akbar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
// 这就是加载类路径下的配置文件
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) app.getBean("dataSource");
System.out.println(dataSource);
}
}
根据绝对路径来加载
// 绝对路径加载
ApplicationContext app = new FileSystemXmlApplicationContext("E:\Java-Project\practice\ssm\ssm01\src\main\resources\applicationContext.xml");
获取Bean的方式
一般获取方式
DataSource dataSource = (DataSource) app.getBean("dataSource");
这种获取方式需要强转
指定类型的字节码文件
DataSource dataSource = app.getBean("dataSource", DataSource.class);
直接指定类型
DataSource dataSource = app.getBean(DataSource.class);
容器中只能有一种Bean。
注解开发
首先需要写下面的配置:
<context:component-scan base-package="com.akbar"/>
@Component
@Component相当于
<bean id="userDao" class="com.akbar.dao.impl.UserDaoImpl"/>
@Component里面可以给Bean起个名字:
@Component("userDao")
如果@Component没有写Bean的名字,获取Bean的时候要用类型获取:
UserDao userDao = (UserDao) app.getBean(UserDao.class);
如果起了个名字,就可以根据名字获取:
UserDao userDao = (UserDao) app.getBean("userDao");
@Component衍生注解
下面的这些注解本质上跟@Component差不多,就是名字更直观一些:
@Sservice:在Service层实现类上加@Repository:在持久层实现类上加@Controller:在表现层上加
纯注解开发
我们可以建一个包叫config,然后里面建一个类:SpringCofig:
package com.akbar.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
// @Configuration注解相当于整个xml配置文件
@ComponentScan("com.akbar")
// @ComponentScan("com.akbar")这个相当于xml配置文件中的 <context:component-scan base-package="com.akbar"/>
public class SpringConfig {
}
现在可以把xml配置文件删掉了
然后启动类怎么写呢?
package com.akbar;
import com.akbar.config.SpringConfig;
import com.akbar.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
// 注意这里AnnotationConfigApplicationContext(SpringConfig.class);
ApplicationContext app =new AnnotationConfigApplicationContext(SpringConfig.class);
UserDao userDao = (UserDao) app.getBean("userDao");
System.out.println(userDao);
}
}
如果@ComponentScan()指定多个包路径,可以这样写
@ComponentScan({"com.akbar.dao", "com.akbar.service"})
@Scope配置Bean的依赖范围
package com.akbar.dao.impl;
import com.akbar.dao.UserDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@Repository
@Scope("singleton") // 单例
//@Scope("prototype") // 非单例
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("UserDao insert...,");
}
}
声明周期
需要先导入下面的依赖:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
然后进行编写:
package com.akbar.dao.impl;
import com.akbar.dao.UserDao;
import org.springframework.stereotype.Repository;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("UserDao insert...,");
}
// 初始化时运行
// 相当于构造方法后运行
@PostConstruct
public void init() {
System.out.println("UserDao init...,");
}
// 销毁前运行
@PreDestroy
public void destroy() {
System.out.println("UserDao destroy...,");
}
}
启动类:
package com.akbar;
import com.akbar.config.SpringConfig;
import com.akbar.dao.UserDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext app =new AnnotationConfigApplicationContext(SpringConfig.class);
UserDao userDao = (UserDao) app.getBean(UserDao.class);
System.out.println(userDao);
app.close();
}
}
依赖注入
@Avotowrite:
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
// 自动装配
@Autowired
private BookDao bookDao;
@Override
public void save() {
bookDao.save();
System.out.println("BookServiceImpl save");
}
}
需要提供无参构造方法,不然造不出对象。
简单类型注入:
我们可以使用
@Value注解赋值
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
// 自动装配
@Autowired
private BookDao bookDao;
// 简单类型装配
@Value("akbar")
private String name;
@Override
public void save() {
bookDao.save();
System.out.println("BookServiceImpl save " + name);
}
}
我们利用这个方法可以注入配置文件中的内容
package com.akbar.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.akbar")
// 这里把配置文件的名称写上去
@PropertySource("jdbc.properties")
public class SpringConfig {
}
package com.akbar.service.impl;
import com.akbar.dao.BookDao;
import com.akbar.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
// 自动装配
@Autowired
private BookDao bookDao;
// 使用配置文件中的值
@Value("${name}")
private String name;
@Override
public void save() {
bookDao.save();
System.out.println("BookServiceImpl save " + name);
}
}
配置文件的完整写法:
@PropertySource("classpath:jdbc.properties")
第三方的Bean管理
比如配置Druid:
在spring的配置类中进行手动编写:
package com.akbar.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
// 定义一个方法获得要管理的Bean
// 让这个方法的返回值定义成一个Bean
//@Bean("dataSource")
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/book?useSSL=false");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
}
然后在启动类中使用:
package com.akbar;
import com.akbar.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
ApplicationContext app =new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource ds = app.getBean(DataSource.class);
System.out.println(ds);
}
}
这种spring的配置类中编写第三方的配置信息不推荐,有没有好的解决办法,我们可以把第三方依赖单独写一个配置文件:
单独建一个JdbcConfig类:
package com.akbar.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class JdbcConfig {
// 定义一个方法获得要管理的Bean
// 让这个方法的返回值定义成一个Bean
//@Bean("dataSource")
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/book?useSSL=false");
ds.setUsername("root");
ds.setPassword("123456");
return ds;
}
}
然后在spring的配置文件中用@Import来导入:
package com.akbar.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
// 导入别的配置类
@Import(JdbcConfig.class)
public class SpringConfig {
}
如果第三方Bean需要别的Bean怎么办?
package com.akbar.config;
import com.akbar.dao.BookDao;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class JdbcConfig {
@Value("com.mysql.jdbc.Driver")
private String driverClassName;
@Value("jdbc:mysql://localhost:3306/book?useSSL=false")
private String url;
@Value("root")
private String username;
@Value("123456")
private String password;
@Bean
public DataSource dataSource(BookDao bookDao) { // 这里直接用形参的方式写就可以,spring自动进行装配
System.out.println(bookDao);
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driverClassName);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
return ds;
}
}
package com.akbar.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("com.akbar")
@Import(JdbcConfig.class)
public class SpringConfig {
}
启动类:
package com.akbar;
import com.akbar.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
public class App {
public static void main(String[] args) {
ApplicationContext app =new AnnotationConfigApplicationContext(SpringConfig.class);
DataSource ds = app.getBean(DataSource.class);
System.out.println(ds);
}
}
spring整合mybatis和junit
视频链接:
AOP面向切面编程
AOP的作用:不惊动原始设计的基础上,对代码进行增强,
这是spring的无侵入式的编程思想。
AOP入门
spring的依赖中默认就包含aop的依赖:
所以,我们只需要到以下aspect包就可以了:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
下面以打印系统时间为例,演示aop:
package com.akbar.dao.impl;
import com.akbar.dao.BookDao;
import org.springframework.stereotype.Repository;
@Repository
public class BookDaoImpl implements BookDao{
@Override
public void save() {
System.out.println("BookDaoImpl save");
}
// 这个方法中的执行的时候(当里面的逻辑只想之前)用aop获取系统时间
@Override
public void update() {
System.out.println("bookDaoImpl update ");
}
}
在aop包下创建MyAdvice类:
package com.akbar.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component // 必须被spring控制
@Aspect //告诉spring 当iop来处理,不要当普通的Bean处理
public class MyAdvice {
// 1.定义切入点
// 当执行到void com.akbar.dao.BookDao.update()这个方法的时候做什么
@Pointcut("execution(void com.akbar.dao.BookDao.update())")
private void pt() {
}
// 2.共性功能
// 方法的什么位置执行
@Before("pt()")
public void message() {
System.out.println(System.currentTimeMillis());
}
}
别忘了在spring的配置类中告诉spring,我们是用注解的方式定义的aop:
package com.akbar.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.akbar")
@EnableAspectJAutoProxy() // 告诉spring,我们是用注解写的aop
public class SpringConfig {
}
启动类:
package com.akbar;
import com.akbar.config.SpringConfig;
import com.akbar.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app =new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = app.getBean(BookDao.class);
bookDao.update();
}
}
切入点表达式
Aop通知类型
- 前置通知:
@Before
package com.akbar.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component // 必须被spring控制
@Aspect //告诉spring 当iop来处理,不要当普通的Bean处理
public class MyAdvice {
// 1.定义切入点
// 当执行到void com.akbar.dao.BookDao.update()这个方法的时候做什么
@Pointcut("execution(void com.akbar.dao.BookDao.update())")
private void pt() {
}
// 2.共性功能
// 方法的什么位置执行
@Before("pt()")
public void message() {
System.out.println(System.currentTimeMillis());
}
}
- 后置通知:
@After
package com.akbar.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component // 必须被spring控制
@Aspect //告诉spring 当iop来处理,不要当普通的Bean处理
public class MyAdvice {
// 1.定义切入点
// 当执行到void com.akbar.dao.BookDao.update()这个方法的时候做什么
@Pointcut("execution(void com.akbar.dao.BookDao.update())")
private void pt() {
}
// 2.共性功能
// 方法的什么位置执行
@After("pt()")
public void message() {
System.out.println(System.currentTimeMillis());
}
}
- 环绕通知:
@Around
package com.akbar.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component // 必须被spring控制
@Aspect //告诉spring 当iop来处理,不要当普通的Bean处理
public class MyAdvice {
// 1.定义切入点
// 当执行到void com.akbar.dao.BookDao.update()这个方法的时候做什么
@Pointcut("execution(void com.akbar.dao.BookDao.update())")
private void pt() {
}
// 2.共性功能
// 方法的什么位置执行
// 原始方法可能有返回值,所以@Around通知的返回值应该写Object
@Around("pt()")
public Object message(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("before " + System.currentTimeMillis());
// 表示对原始操作的调用,拿到它的返回值
Object proceed = joinPoint.proceed();
System.out.println("after " + System.currentTimeMillis());
// 然后把返回值返回出去
return proceed;
}
}
获取连接点的信息:
package com.akbar.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component // 必须被spring控制
@Aspect //告诉spring 当iop来处理,不要当普通的Bean处理
public class MyAdvice {
// 1.定义切入点
// 当执行到void com.akbar.dao.BookDao.update()这个方法的时候做什么
@Pointcut("execution(void com.akbar.dao.BookDao.update())")
private void pt() {
}
// 2.共性功能
// 方法的什么位置执行
// 原始方法可能有返回值,所以@Around通知的返回值应该写Object
@Around("pt()")
public Object message(ProceedingJoinPoint joinPoint) throws Throwable {
// joinPoint就是链接点,我们可以通过这个连接点获取方法各种信息
Signature signature = joinPoint.getSignature();
System.out.println(signature.getDeclaringType()); // 输出interface com.akbar.dao.BookDao
System.out.println(signature.getName()); // 输出update,也就是方法名
System.out.println(signature.getDeclaringTypeName()); // com.akbar.dao.BookDao
System.out.println("before " + System.currentTimeMillis());
// 表示对原始操作的调用,拿到它的返回值
Object proceed = joinPoint.proceed();
System.out.println("after " + System.currentTimeMillis());
// 然后把返回值返回出去
return proceed;
}
}
可以慢慢研究:
// joinPoint就是链接点,我们可以通过这个连接点获取方法各种信息 Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringType()); // 输出interface com.akbar.dao.BookDao System.out.println(signature.getName()); // 输出update,也就是方法名 System.out.println(signature.getDeclaringTypeName()); // com.akbar.dao.BookDao
- 返回后通知:
@AfterReturning
没有抛异常,正常结束以后运行。
- 抛异常以后执行:
@AfterThrowing
Spring事务
视频地址:
Spring MVC
什么是spring MVC?
@ResponseBody
@ResponseBody表示:
将 Controller 方法的返回值** 直接作为 HTTP 响应体返回给前端,而不是跳转到视图(页面)。
我们以前controller类上经常写@ResponseBody和@Controller,但是这个可以简化:
@RestController // 等价于 @Controller + @ResponseBody
public class UserController {
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "Hello Spring!";
}
@RequestMapping("/user")
@ResponseBody
public User getUser() {
return new User("张三", 20);
}
}
异常处理
package com.akbar.exception;
import com.akbar.util.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice // 声明这个类是处理异常的类
public class GlobalExceptionHandler {
// 想处理什么异常,这里要指定
@ExceptionHandler(Exception.class)
public void handleException(Exception e) {
System.out.printl("出异常了");
}
}
注意,要保证这个类被spring扫描到。
这个全局异常处理的思想是,把所有异常都抛到表现层controller,由controller统一处理。
给前端返回个性化信息:
package com.akbar.exception;
import com.akbar.util.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice // 声明这个类是处理异常的类
public class GlobalExceptionHandler {
// 想处理什么异常,这里要指定
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
return Result.error("出异常了");
}
}