一、Spring
- Core Container:核心容器
- Aspects:AOP思想实现
- AOP:面向切面
- Data Integration:数据集成
- Data Access:数据访问
- Test:单元测试与集成测试
快速入门
-
IoC(Inversion of Control)控制反转
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
-
Spring技术对IoC思想进行了实现
- 由Spring提供了一个容器,称为IoC容器,来充当IoC思想中的"外部"
- IoC容器负责对象的创建,初始化等一系列,被创建或被管理的对象在IoC容器中统称为Bean
-
DI(Dependency Injection)依赖注入
- 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
-
目标: 充分解耦
- 使用IoC容器管理bean(IoC)
- 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
-
最终效果
- 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
入门案例
- 基于IoC管理bean
- Service中使用new形式创建的Dao对象是否保留?(否)
- Service中需要的Dao对象如何进入到Service中?【使用依赖注入】(提供方法)
- Service与Dao间的关系如何描述?(配置)【一个业务层可以控制很多数据层】
//步骤1:将业务层中的new的DAO对象删除,使用set方法实现
public class BookServiceImpl implements BookService{
private BookDao;
public void save(){
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
}
<!--步骤2:配置bean进行依赖注入,使用property标签注册一个属性-->
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<!--配置dao与service的关系-->
<property name="bookDao" ref="bookDao"/>
</bean>
使用Spring的API获得Bean实例
public class UseDaoDemo {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
}
}
二、Bean相关配置
Bean的别名
在application.xml起别名,或取多个别名,【用空格,或者逗号,或者分号分隔开来】
ref属性既可以引用id也可以引用name,getBean可使用别名
<bean id="bookDao" name="dao dao2" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service" class="com.hcx.service.impl.BookServiceImpl">
<!--配置dao与service的关系-->
<property name="bookDao" ref="dao"/>
</bean>
scope:指对象的作用范围
- 为什么
bean默认的是单例的?
---便于管理复用对象,提高效率
-
适合交给容器进行管理的bean
- 封装实体的域对象(有状态的)
Bean的生命周期
-
生命周期:从创建到消亡的完整过程
-
bean生命周期:bean从创建到销毁的整体过程
-
bean生命周期控制:在bean创建后到销毁前做一些事情
-
配置
- init-method
- destory-method
public void init(){
System.out.print("Bean初始化操作");
}
public void destory(){
System.out.print("Bean销毁操作");
}
<bean id="bookDao" class="com.hcx.impl.BookDaoImpl" init-method="init" destory-method="destory"/>
//无法调用销毁方式原因,JVM直接退出,未执行关闭ClassPathXmlApplicationContext对象
eg:
// 不用 ApplicationContext接口下ClassPathXmlApplication实现类型下的close()方法 ,还可以使用使用ApplicationContext下的实现类AnnotationConfigApplicationContext()实现下的close()方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao =(BookDao) context.getBean("bookDao");
//暴力方法
context.close();
//灵巧方法
context.registerShutdownHook();
-
接口
- InitializingBean
- DisposableBean
//bean生命周期,标准实现方式,实现InitializingBean, DisposableBean两个接口中的方法
eg:
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
// 删除业务层中使用new的方式创建dao对象
// private BookDao bookDao = new BookDaoImpl();
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
System.out.println("提供了对应的set方法");
}
@Override
public void destroy() throws Exception {
System.out.println("service destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
Bean实例化方式
- Bean本质上就是对象,创建bean使用构造方法完成
方法一:构造方法实现Bean
//首先在application中注册bean
//其次在实现类中实现构造方法实例化实现类
private BookDaoImpl(){
System.out.println("Book is constructor is running ...");
}
//无论是使用private还是public都能访问到构造方法,利用到了反射
//无参构造方法是默认实现
<bean id="bookkDao" class="com.hcx.dao.impl.BookDaoImpl"/>
方法二:使用静态工厂
//新建一个静态工厂
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}
//实例化对象时getBean()方法调用静态工厂实例化对象
<bean id="orderDao" class="com.hcx.factory.OrderDaoFactory" factory-method="getOrderDao"/>
方法三:实例工厂
public class AppForInstanceUser{
public static void main(String[] args){
//创建实例静态工厂对象
UserDaoFactory userDaoFactroy = new UserDaoFactory();
//通过实例工厂对象创建对象
User userDao = userDaoFactory.getUserDao();
userDao.save();
}
}
public class UserDaoFactory{
public UserDao getUserDao(){
retrun new UserDaoImpl();
}
}
<bean id="userFactory" class="com.hcx.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
方式四:FactoryBean
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
public UserDao getObject() throws Expection{
return new UserDaoImpl();
}
public Class<?> getObjectType(){
return UserDao.class;
}
}
<bean id="userDao" class="com.hcx.factory.UserDaoFactoryBean"/>
依赖注入方式
1)set方法注入
引用类型
-
在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService( private BookDao bookDao; public void setBookDao(BookDao bookDao){ this.bookDao = bookDao; } ) -
配置中使用property标签ref属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDao"/> </bean> <bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
简单类型
- 在bean中定义简单类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao(
private int connectionNum;
private String databaseName;
public void setConnectionNum(int connectionNum){
this.connectionNum=connectionNum;
}
public void setDatabaseName(String databaseName){
this.databaseName=datanaseName;
}
)
- 配置中使用property标签value属性注入基本类型对象
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<property name="connectionNum" value="100"/>
<property name="databaseName" value="MySql"/>
</bean>
2)构造方法注入
引用类型
- 在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService(
private BookDao bookDoa;
public BookServiceImpl(BookDao bookDao){
this.bookDao = bookDao;
}
)
- 配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
构造器注入——简单类型
- 在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao{
private int connectionNumber;
public BookDaoImpl(int connectionNumber){
this.connectionNumber = connectionNumber;
}
}
- 在配置中使用constructor-arg标签value属性注入简单数据类型
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNumber" value="10"/>
</bean>
参数设置
- 配置中使用constructor-arg标签type属性设置按形参类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
- 配置中使用constructor-arg标签index属性设置按形参位置注入
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
<constructor-arg index="0" value="10"/>
<constructor-arg index="1" value="MYSQL"/>
</bean>
依赖自动装配
-
oC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称之为自动装配
-
自动装配方式
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
-
配置中使用bean标签autowire属性设置自动装配的类型
**
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl" autowire="byType"/>
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名和配置耦合,不推荐使用
- 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配失效
集合注入 数据类型:
数组
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
List
<property name="list">
<list>
<value>加油</value>
<value>每天</value>
<value>学习</value>
</list>
</property>
Set
<property name="set">
<set>
<value>好好</value>
<value>工作</value>
<value>跑路</value>
</set>
</property>
Map
<property name="map">
<map>
<entry key="country" value="china"></entry>
<entry key="city" value="成都"></entry>
<entry key="province" value="四川"></entry>
</map>
</property>
Properties
<property name="properties">
<props>
<prop key="country">中国</prop>
<prop key="city">南昌</prop>
<prop key="province">江西</prop>
</props>
</property>
Spring重要API
ApplicationContext app = new ClasspathXmlApplicationContext("xml文件")
app.getBean("id")
app.getBean(Class)
三、案例数据源对象管理
- 导入druid坐标、导入c3p0坐标和mysql连接坐标
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
- 配置数据源对象作为spring管理的bean
<!-- 管理DruidDataSource对象-->
<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/message"/>
<property name="username" value="root"/>
<property name="password" value="505489"/>
</bean>
- 配置数据源对象作为spring管理的bean
<!-- 管理c3p0对象-->
<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/message"/>
<property name="user" value="root"/>
<property name="password" value="505489"/>
</bean>
- 加载properties文件
- 首先开启命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
2.使用context空间加载properties文件
<context:property-placeholder location="jdbc.properties"/>
3.使用属性占位符${}读取properties文件中的属性
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.Driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
- 方式一:路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 方式二:文件路径加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\applicationContext.xml");
- 方式三:读取web下的资源文件
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
- 加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
获取bean
- 方式一:使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
- 方式二:使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
- 方式三:使用bean类型获取
BookDao bookDao = ctx.getBean(BookDao.class);
容器层次结构
总览 BeanFactory 体系,按照接口的抽象层次,大体可以分层四层:
- 第一层:
BeanFactory; - 第二层:
HierarchicalBeanFactory,ListableBeanFactory,AutowireCapableBeanFactory; - 第三层:
ConfigurableBeanFactory,此外还有一个关联性较强SingletonBeanRegistry; - 第四层:
ConfigurableListableBeanFactory;
BeanFactory初始化
- 类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean("bookDao",BookDao.class);
bookDao.save();
注意:BeanFactory创建完毕后,所有的bean均为延时加载
延时加载好处:当为了追求传输效率就会需要什么就再去创建什么时,就会体现出延迟加载的好处,有一个缓冲时间。
四、IOC和DI注解开发
注解开发-定义Bean
- 使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
- 核心配置文件中通过组件扫描加载bean
<context:component-scan base-package="com.hcx"/>
-
Spring提供@Component注解的三个衍生注解
- @Controller:用于表现层bean定义
- @Service:用于业务层bean定义
- @Repository:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
}
@Service
public class BookServiceImpl implements BookService{
}
核心配置文件替换
- Spring 3.0 升级了纯注解开发模式,使用了Java类代替配置文件,开启了Spring快速开发赛道
- Java类代替Spring核心配置文件,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.springframework.org/schema/beans/spring-beans.xsd">
<context:component-scan base-package="com.hcx"/>
</beans>
被
@Configuration
@ComponentScan("com.hcx")
public class SpringConfig{
}
代替
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({"com.hcx.service","com.hcx.dao"})
- 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
//加载配置文件初始化容器(xml版)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
自动装配
- 使用@Autowired注解开启自动装配模式(按类型)
@Servcie
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
public void setBookDao(BookDao bookDao){
this.bookDao=bookDao;
}
public void save(){
System.out.print("book service save ...");
bookDao.save();
}
}
- 注意:自动装配基于反射设计创建对象并暴力反射对应属性初始化数据,因此无需提供setter()方法
- 注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
- 使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService{
@Autowired
@Qualifier("bookDao")
private BookDao bookDAO;
}
- 注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
简单数据类型注入
在定义的数据类型上放使用@Value("值")
@Value("学习")
private String action;
加载外部properties文件
- 在SpringConfig类下,使用@PropertySource注解加载properties文件
@Configuration
@ComponentScan("com.hcx")
@uPropertySource("classpath:jdbc.properties")
public class SpringConfig{}
- 在给属性注入值时
@Value("${外部文件中定义的属性名}")
private String value;
- 注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符
第三方Bean管理
- 使用@Bean配置第三方bean
@Configuration
public class SpringConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
//不推荐
- 将独立的配置类加入核心配置
- 方式一:导入式
public class JdbcConfig{
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
//相关配置
return ds;
}
}
- 使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig{
}
第三方Bean依赖注入
- 简单类型依赖注入
public class JdbcConfig{
@Value("com.mysql.jdbc.Driver")
private String dirver;
@Value("jdbc:mysql://localhost:3306/spring_db")
private String url;
@Value("root")
private String userName;
@Value("505489")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDatSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUserName(userName);
ds.setPassword(password);
return ds;
}
}
- 引用类型依赖注入
@Bean
public DataSource dataSource(BookService bookService){
Sytstem.out.print(bookService);
DruidDataSource ds = new DruidDataSource();
//属性设置
return ds;
}
- 引用类型注入只需要为bean定义方法设置形参即可,容器会更卷类型自动装配对象
创建Dao接口(用注解的形式)
步骤四:创建Service接口和实现类
步骤五:添加jdbc.properties配置文件
步骤六:创建SpringConfig、MybatisConfig、JdbcConfig配置类
步骤七:编写运行类
五、AOP
-
AOP(Aspect Oriented Programming)面向切面编程,是一种编程范式,指导开发者如何组织程序结构
- OOP(Object Oriented Programming)面向对象编程
-
作用:在步惊动原始设计的基础上进行功能增强,追加功能
-
Spring理念:无侵入式
核心概念
- 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
- 代理(Proxy):目标对象无法直接工作,需要对其进行功能会填,通过原始对象的代理对象实现
- SpringAOP本质:代理模式
连接点与切入点
-
连接点:执行的方法,程序执行过程中的任意位置,可以为方法,抛出异常,也可以为设置变量。
-
切入点:需要被增强的方法,一个切入点可以描述一个具体的方法,也可以匹配多个方法,一个切入点可以只匹配一个update方法,也可以匹配某个包下面所有的查询方法。
-
【连接点范围 > 切入点范围,切入点一定是连接点,反之未必】
通知与通知类
-
通知:存放共性功能的方法,在切入点处执行的操作,也就是共性功能。
-
通知类:定义通知的类,通知是一个方法,方法不能独立存在,需要被写在一个类中,这个类我们也给起了个名字叫通知类
切面
- 切面:通知与切入点之间的关系描述
通知是要增强的内容,会有多个;切入点是需要被增强的方法,也会有多个,哪个切入点需要添加哪个通知,就需要提前将它们之间的关系描述清楚,那么对于通知和切入点之间的关系描述,我们称之为切面
入门案例
- 导入aop相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
说明:Spring-context坐标依赖spring-aop坐标
2.定义dao接口与实现类
public interface BookDao{
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao{
public void save(){
System.out.println(System.currentTimeMills());
System.out.println("book dao save...");
}
public void update(){
System.out.println("book dao update...");
}
}
3.定义通知类,制作通知
public class MyAdvice{
public void before(){
System.out.println(System.currentTimeMills());
}
}
4.定义切入点
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
}
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
5.绑定切入点与通知关系,并指定通知添加到原始连接点的具有执行位置
public class MyAdvice{
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currenTimeMillis());
}
}
6.定义通知类受Spring容器管理,并定义当前类为切面类
@Componet
@Aspect
public class MyAdvice{
@Pointcut("execution(void com.hcx.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void before(){
System.out.println(System.currentTimeMillis());
}
}
7.开启Spring对AOP注解驱动支持
@Configuration
@ComponetScan("com.hcx")
@EnableAspectAutoProxy
public class SpringConfig{
}
通知类型
那么当对findName()进行增强的时候,通知类中的共性方法如何才能获取findName中的参数id呢?
使用JoinPoint 通知作如下修改:
@Before("pt()")
public void before(JoinPoint jp)
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("before advice ..." );
}
我们在执行类执行findName(100)时,输出如下:
可见,共性方法成功得到了执行方法中的id参数,并且以数组的形式进行了输出(因为方法中的形参可能有多个)
如果执行方法为findName(id, password),在执行类执行findName(100,itheima),那么输出为:
-
@Around注意事项
- 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
- 通知中如果为使用ProceedIngJoinPoint对原始方法进行调用将跳过原始方法的执行
- 对原始方法的调用可以不接收返回值,通知方法设置称void即可,如果接收返回值,必须设定为Object类型
- 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
- 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
**
@Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around before advice ..."); Object ret = pjp.proceed(); System.out.println("around after advice ..."); return ret; }-
名称:@AfterReturning(了解)
-
类型:方法注解
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
-
范例:
@AfterReturning("pt()") public void after Returning(){ System.out.println("afterReturning advice ..."); } -
相关属性:value(默认):切入点方法名,格式为类名.方法名()
-
名称:@AfterThrowing(了解)
-
类型:方法注解
-
位置:通知方法定义上方
-
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
-
范例:
@AfterThorwing("pt()") public void afterThorwing(){ System.out.println("afterThrowing advice ..."); } -
相关属性:value(默认):欺辱点方法名,格式为:类名.方法名()