1. 概念理解
1.1 Spring
Spring 是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。简化企业级开发,支持对框架的整合。
1.2 IOC 控制反转
IOC(Inversion of Control)控制反转:
是一种设计思想,由主动的创建变成被动的接收,即程序本身不创建对象,而是被动的接受对象;
控制是指由谁控制对象的创建,传统应用程序的对象是由程序本身控制对象的创建,而Spring 框架是由Spring 来创建,放到容器中,使用时从容器中取对象即可;
DI(Dependency Injection)依赖注入:
是实现IOC 的一种方法,就是对属性的注入(赋值),不用再管理对象的创建,大大降低了系统的耦合性,本质是利用Set 方法来进行注入。
1.3 AOP 面向切面编程
AOP(Aspect Oriented Programming)面向切面编程:
通过预编译方式和动态代理实现程序功能的统一维护的一种技术。AOP 是OOP 的延续,而代理模式是Spring AOP 的底层。
开发工程中,部分代码与业务逻辑无关,但又需要使用他们,如事物、日志(前后)、缓存、安全等代码,利用AOP 就不必将这部分代码写入业务逻辑代码中。
1.4 框架整合
SSH:Struts + Spring + Hibernate
SSM:SpringMvc + Spring + Mybatis
Spring Boot:快速开发单个微服务,约定大于配置;
Spring Cloud:基于Spring Boot 实现的,协调整合微服务。
2. 代码理解 IOC
客户端 ⇒ Service ⇒ Dao 三层调用
导入jar 包:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
2.1 传统写法
客户端调用Dao 层的具体操作时,需要Service 层创建Dao 对象,客户端再创建Service 对象,从而进行调用操作。
需求:获取 MySql 数据库连接。
// Dao 层
public interface JDBCDao {
void getConnect();
}
public class MySqlDaoImpl implements JDBCDao {
public void getConnect() {
System.out.println("===MySqlDaoImpl===");
}
}
// Service 层
public interface JDBCService {
void getConnect();
}
public class JDBCServiceImpl implements JDBCService {
private JDBCDao jdbcDao = new MySqlDaoImpl();
public void getConnect() {
jdbcDao.getConnect();
}
}
// 客户端(测试类)
public class MyTest {
public static void main(String[] args) {
JDBCService jdbcService = new JDBCServiceImpl();
jdbcService.getConnect();
}
}
不便之处举例:
当需求变更,需要获取 Oracle 数据库连接时,Service 层需要从新创建Dao 实现类的对象。
// 增加Oracle 的 Dao 实现类
public class OracleDaoImpl implements JDBCDao {
public void getConnect() {
System.out.println("OracleDaoImpl Dao");
}
}
// Service 层需要从新创建Dao 接口实现类对象 OracleDaoImpl()
public class JDBCServiceImpl implements JDBCService {
// private JDBCDao jdbcDao = new MySqlDaoImpl();
private JDBCDao jdbcDao = new OracleDaoImpl();
public void getConnect() {
jdbcDao.getConnect();
}
}
2.2 普通代码的控制反转
Service 层不再控制Dao 对象的创建,而是交由调用者来创建,客户端与业务逻辑中关于操控对象的创建职责发生反转,顾称之为“控制反转”。从而大大降低了系统的耦合性。
// Service 层将不创建Dao 对象,而让调用者通过set 方法自行设置要调用的Dao 实现类对象
public class JDBCServiceImpl implements JDBCService {
private JDBCDao jdbcDao;
public void setJdbcDao(JDBCDao jdbcDao) {
this.jdbcDao = jdbcDao;
}
public void getConnect() {
jdbcDao.getConnect();
}
}
// 客户端创建Dao 对象,并进行调用
public class MyTest {
public static void main(String[] args) {
JDBCService jdbcService = new JDBCServiceImpl();
//((JDBCServiceImpl)jdbcService).setJdbcDao(new MySqlDaoImpl());
((JDBCServiceImpl)jdbcService).setJdbcDao(new OracleDaoImpl());
jdbcService.getConnect();
}
}
2.3 Spring 的控制反转
Spring 容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc 容器中取出需要的对象。
通过XML 配置文件控制对象的创建。
2.3.1 简例
Hello 类
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
创建配置文件:resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建对象 -->
<bean id="hello" class="com.chengyu.pojo.Hello">
<!-- 属性赋值 -->
<property name="name" value="Spring"/>
</bean>
</beans>
客户端调用:从容器中取对象!
public class MyTest {
public static void main(String[] args) {
// 获取Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象在Spring 容器中管理,从容器中取对象
Hello hello = (Hello)context.getBean("hello");
System.out.println(hello.toString());
}
}
2.3.2 将2.2 代码升级为Spring 管理
创建配置文件:resources/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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 创建Dao 对象 -->
<bean id="mySqlDaoImpl" class="com.chengyu.dao.MySqlDaoImpl"/>
<bean id="oracleDaoImpl" class="com.chengyu.dao.OracleDaoImpl"/>
<bean id="jdbcServiceImpl" class="com.chengyu.service.JDBCServiceImpl">
<!-- ref 引用Spring 中已经创建好的对象 -->
<property name="jdbcDao" ref="oracleDaoImpl"/>
</bean>
</beans>
客户端调用:
public class MyTest {
public static void main(String[] args) {
// 获取Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象在Spring 容器中管理,从容器中取对象
JDBCServiceImpl jdbcService = (JDBCServiceImpl)context.getBean("jdbcServiceImpl");
jdbcService.getConnect();
}
}
3. Bean 管理(XML方式)
Bean 中主要做两件事,一是创建对象,二是注入属性。
3.1 创建对象
例:在Bean 中对Hello 类进行对象的创建。
public class Hello {
private String name;
public Hello() {
}
public Hello(String name) {
this.name = name;
}
// set get 方法略
}
3.1.1 无参构造器(默认)
<!-- 创建对象 -->
<bean id="hello" class="com.chengyu.pojo.Hello"></bean>
3.1.2 有参构造器 - 下标赋值
<!-- 创建对象 -->
<bean id="hello" class="com.chengyu.pojo.Hello">
<!-- 下标赋值 -->
<constructor-arg index="0" value="chengyu"/>
</bean>
3.1.3 有参构造器 - 类型赋值
<!-- 创建对象 -->
<bean id="hello" class="com.chengyu.pojo.Hello">
<!-- 类型赋值 -->
<constructor-arg type="java.lang.String" value="chengyu1"/>
</bean>
3.1.4 有参构造器 - 参数名赋值
<!-- 创建对象 -->
<bean id="hello" class="com.chengyu.pojo.Hello">
<!-- 参数名赋值 -->
<constructor-arg name="name" value="chengyu2"/>
</bean>
3.2 属性注入(赋值)DI - 手动装配
DI 依赖注入,是实现IOC 的一种方法,就是属性注入的意思。
3.2.1 构造器注入属性
【3.1】有参构造器创建时,对属性的注入。
3.2.2 Set 方法注入属性 ※
依赖:bean 对象的创建依赖于容器
注入:bean 对象中的所有属性,由容器来注入
public class Address {
private String address;
// set get 方法略
}
public class Person {
private String name;
private Address address;
private String[] arrays;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
private Properties properties;
private String bak;
// set get 方法略
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.chengyu.pojo.Address">
<property name="address" value="大连"/>
</bean>
<bean id="person" class="com.chengyu.pojo.Person">
<!-- 字符串注入 -->
<property name="name" value="chengyu3"/>
<!-- bean 注入 -->
<property name="address" ref="address"/>
<!-- 数组 注入 -->
<property name="arrays">
<array>
<value>aaaArr</value>
<value>bbbArr</value>
<value>cccArr</value>
</array>
</property>
<!-- List 注入 -->
<property name="list">
<list>
<value>aaaList</value>
<value>bbbList</value>
</list>
</property>
<!-- Map 注入 -->
<property name="map">
<map>
<entry key="key1" value="value1"/>
<entry key="key2" value="value2"/>
</map>
</property>
<!-- Set 注入 -->
<property name="set">
<set>
<value>aaaSet</value>
<value>bbbSet</value>
</set>
</property>
<!-- null 注入 -->
<property name="bak">
<null/>
</property>
<!-- Properties 注入 -->
<property name="properties">
<props>
<prop key="学号">0907</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person)context.getBean("person");
System.out.println(person.toString());
}
Person{name=‘chengyu3’, address=Address{address=‘大连’}, arrays=[aaaArr, bbbArr, cccArr], list=[aaaList, bbbList], map={key1=value1, key2=value2}, set=[aaaSet, bbbSet], properties={学号=0907, 性别=男}, bak=‘null’}
3.2.3 扩展方式注入
可以使用 p 命名空间和 c 命名空间注入。
public class User {
private String name;
private int age;
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
// set get 方法略
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p 命名空间注入,可以直接注入属性值-->
<bean id="userP" class="com.chengyu.pojo.User" p:name="chengyu" p:age="30"/>
<!--c 命名空间注入,可以直接注入构造器-->
<bean id="userC" class="com.chengyu.pojo.User" c:name="chengwei" c:age="25"/>
</beans>
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User userP = (User)context.getBean("userP");
System.out.println(userP);
User userC = (User)context.getBean("userC");
System.out.println(userC);
}
3.3 属性注入(赋值)DI - 自动装配
自动装配:不用使用property 属性标签, 根据匹配的属性值进行注入。
Spring 会在上下文中自动寻找,并自动给bean 装配属性。
例:一个人有两个宠物。
public class Cat {
public void eat(){System.out.println("吃鱼");}
}
public class Dog {
public void eat(){System.out.println("啃骨头");}
}
public class Person {
private String name;
private Cat cat;
private Dog dog;
// set get 方法略
}
<bean id="cat" class="com.chengyu.pojo.Cat"/>
<bean id="dog" class="com.chengyu.pojo.Dog"/>
<bean id="person" class="com.chengyu.pojo.Person">
<property name="name" value="chengyu"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person)context.getBean("person");
person.getCat().eat();
person.getDog().eat();
}
3.3.1 自动装配:autowire=“byName”
会自动在容器上下文查找和自己对象set() 后面的值对应的 beanid。
<bean id="cat" class="com.chengyu.pojo.Cat"/>
<bean id="dog" class="com.chengyu.pojo.Dog"/>
<bean id="person" class="com.chengyu.pojo.Person" autowire="byName">
<property name="name" value="chengyu"/>
</bean>
3.3.2 自动装配:autowire=“byType”
会自动在容器上线文查找和自己对象属性类型相同的 beanid。
<bean id="cat" class="com.chengyu.pojo.Cat"/>
<bean id="dog" class="com.chengyu.pojo.Dog"/>
<bean id="person" class="com.chengyu.pojo.Person" autowire="byType">
<property name="name" value="chengyu"/>
</bean>
3.4 Bean 的作用域 scope
singleton 单例模式,默认。
<bean id="address" class="com.chengyu.pojo.Address" scope="singleton">
<property name="address" value="大连"/>
</bean>
prototype 原型模式,每次从容器中 get 的时候,都是一个新对象。
<bean id="address" class="com.chengyu.pojo.Address" scope="prototype">
<property name="address" value="大连"/>
</bean>
request、session、application 只能在web 开发中使用。
3.5 补充
3.5.1 起别名-alias方式
<bean id="hello" class="com.chengyu.pojo.Hello">
<constructor-arg name="name" value="chengyu2"/>
</bean>
<alias name="hello" alias="hi"/>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 对象在Spring 容器中管理,从容器中取对象
//Hello hello = (Hello)context.getBean("hello");
Hello hello = (Hello)context.getBean("hi");
System.out.println(hello.toString());
}
3.5.2 起别名-name方式
name 属性也可以起别名,而且可以多个。
<bean id="hello" class="com.chengyu.pojo.Hello" name="hi,hell">
<constructor-arg name="name" value="chengyu2"/>
</bean>
3.5.3 合并配置文件 import
一般用于团队开发使用,可以将多个配置文件导入并合并。
applicationContext.xml 中导入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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"/>
</beans>
4. Bean 管理(注解方式)
Bean 中主要做两件事,一是创建对象,二是注入属性。
前提:
1)Spring2.5 开始支持,在Spring4 之后,要使用注解开发,必须导入 aop 包。(aspectjweaver 包中已包含)
2)开启组件扫描:
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
<context:annotation-config/>
4.1 创建对象 Component
@Component
等价于创建了一个bean,即创建对象。
<bean id=“user” class=“com.chengyu.pojo.User”/>
@Component
public class User {
}
衍生注解:功能相同,仅为了提示代码所处的层不同。
dao:@Repository
service:@Service
controller:@Controller
@Repository
public class UserDao {
}
@Service
public class UserService {
}
@Controller
public class UserController {
}
这四个注解的功能是一样的,都是将某个类注册到Spring 容器中,装配Bean。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.chengyu"/>
</beans>
4.2 属性注入DI - 手动装配
@Value:
// 等价于 bean 中的属性注入 <property name="name" value="chengyu"/>
@Value("chengyu")
public void setName(String name) {
this.name = name;
}
4.3 属性注入DI - 自动装配
4.3.1 Autowired
Spring 会在上下文中自动寻找,并自动给bean 装配属性。
<bean id="cat" class="com.chengyu.pojo.Cat"/>
<bean id="dog" class="com.chengyu.pojo.Dog"/>
<bean id="person" class="com.chengyu.pojo.Person"/>
@Autowired
public class Person {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
// set get 方法略
}
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person)context.getBean("person");
person.getCat().eat();
person.getDog().eat();
}
补充:
1)@Autowired 可以和 @Qualifier(value = “cat”) 搭配使用,指明唯一的bean 对象注入。
@Autowired
@Qualifier(value = "cat")
private Cat cat;
2)@Resource 根据Type 查找。类型重复时,也可指定名称 @Resource(name=“cat2”)
4.3.2 Resource
import javax.annotation.Resource;
是Java 的注解,只要名称或类型有一个一致,即可成功注入;
Resource 中也可以指定名称。
<bean id="cat" class="com.chengyu.pojo.Cat"/>
<bean id="dog" class="com.chengyu.pojo.Dog"/>
<bean id="person" class="com.chengyu.pojo.Person"/>
public class Person {
private String name;
@Resource
private Cat cat;
@Resource(name = "dog")
private Dog dog;
// set get 方法略
}
4.4 作用域 Scope
@Controller
@Scope("singleton")
public class UserController {
}
4.5 XML与注解比较
XML 更万能,适用于任何场合,维护简单方便;
注解:不是自己的类使用不了,相对复杂。
推荐:XML 管理Bean,注解实现属性的注入。
注意:使用注解必须先让开启注解支持,并扫描到该注解所在的包。
<!--开启注解支持-->
<context:annotation-config/>
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.chengyu.pojo"/>
5. 纯Java 装配
在Spring 中有三种装配方式:
1)手动装配
2)自动装配
3)Java 中显示装配
完全不需要配置文件,Spring4 之后,javaConfig 成为核心功能。
@Component:将类注册到Spring 容器中。
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("chengyu")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Configuration:注明这是一个配置类,等同于beans.xml
@Bean:相当于一个bean 标签,方法名相当于 id,返回值是要注入的bean 对象
@Configuration
@ComponentScan("com.chengyu.pojo")
public class ChengConfig {
@Bean
public User getUser(){
return new User();
}
}
public void test01(){
ApplicationContext context = new AnnotationConfigApplicationContext(ChengConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user.getName());
}
6. 代码理解 AOP
6.1 API 接口方式
public class Log implements MethodBeforeAdvice, AfterReturningAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");
}
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + method.getName() + ",返回结果为:" + returnValue);
}
}
业务逻辑:
public interface UserService {
void add();
void delete();
void update();
void select();
}
public class UserServiceImpl implements UserService
{
public void add() {
System.out.println("添加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("更新一个用户");
}
public void select() {
System.out.println("查询一个用户");
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册 bean-->
<bean id="userService" class="com.chengyu.service.UserServiceImpl"/>
<bean id="log" class="com.chengyu.log.Log"/>
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop 的约束-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.chengyu.service.UserServiceImpl.*(..))"/>
<!--执行环绕增强-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
</beans>
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
com.chengyu.service.UserServiceImpl的add被执行了
添加一个用户
执行了add,返回结果为:null
6.2 自定义类方式
public class DiyPointCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
业务逻辑:
public interface UserService {
void add();
void delete();
void update();
void select();
}
public class UserServiceImpl implements UserService
{
public void add() {
System.out.println("添加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("更新一个用户");
}
public void select() {
System.out.println("查询一个用户");
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册 bean-->
<bean id="userService" class="com.chengyu.service.UserServiceImpl"/>
<bean id="log" class="com.chengyu.log.Log"/>
<!--方式二:自定义类-->
<bean id="diy" class="com.chengyu.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面:ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.chengyu.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
MyTest 执行结果:
方法执行前
添加一个用户
方法执行后
6.3 注解方式
@Aspect // 标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.chengyu.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.chengyu.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后");
}
// 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.chengyu.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable { // 可以从切入点中获取东西
System.out.println("环绕前");
System.out.println(jp.getSignature());// 获取签名
// 执行
Object SS = jp.proceed();
System.out.println("环绕后");
}
}
业务逻辑:
public interface UserService {
void add();
void delete();
void update();
void select();
}
public class UserServiceImpl implements UserService
{
public void add() {
System.out.println("添加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("更新一个用户");
}
public void select() {
System.out.println("查询一个用户");
}
}
MyTest 执行结果:
环绕前
void com.chengyu.service.UserService.add()
方法执行前
添加一个用户
方法执行后
环绕后