1、join point(连接点):
是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。
2、point cut(切入点):
本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。(Pointcut)
3、advice(通知):
是point cut的执行代码,是执行“方面”的具体逻辑。(BeforeAdvice, AroundAdvice, AfterReturnAdvice, AfterThrowsAdvice)
4、aspect(方面):
切入点point cut和通知advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。
5、introduce(引入):
为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的AOP工具又将其称为mixin。
6、Advisor(通知器):
当我们完成对目标方法的切面增强设计(通知advice)和关注点的设计(切入点pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor。
通过Advisor可以定义应该使用哪个advice并且在哪个pointcut使用它。NameMatchMethodPointcutAdvisor是细化后的DefaultPointcutAdvisor,它限定了自身可以使用的Pointcut类型为NameMatchMethodPointcut,并且外部不可更改。其两个属性名分别为advice和mappedName。
7、ProxyFactoryBean:
ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,也是最灵活的方法,spring通过它完成了对AOP使用的封装。在基于XML配置Spring的Bean的时候,往往需要一系列的配置步骤来使用ProxyFactoryBean和AOP,比如以下步骤:
1) 定义使用的通知器Advisor,这个通知器应该作为一个Bean来定义。
<!-- 拦截器对象 -->
<bean id="methodBeforeAdviceImpl"class="example.MethodBeforeAdviceImpl"/>
<!-- 配置通知器,用的是Spring组件 -->
<bean id="theAdvisor"class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<propertyname="advice">
<reflocal="methodBeforeAdviceImpl"/><!--拦截器对象反射到advice对象(通知)里 -->
</property>
<propertyname="mappedName"value="*"></property><!--拦截所有的方法 -->
</bean>
2) 定义ProxyFactoryBean,把它作为另一个Bean来定义,它是封装AOP功能的主要类。
在配置ProxyFactoryBean时,需要设定与AOP实现相关的重要属性,如interceptorNames和target等。interceptorNames属性的值往往设置为需要定义的通知器,因为这些通知器在ProxyFactoryBean的AOP配置下,是通过使用代理对象的拦截器机制起作用的,所以依然沿用拦截器这个名字。
3) 定义target属性,作为target属性注入的Bean,是需要用AOP通知器中的切面应用来增强的对象。ProxyFactoryBean需要为目标对象生成代理对象。
<!-- ProxyFactoryBean需要为目标对象生成代理对象 -->
<!-- 代理类Spring的组件 -->
<beanid="service"class="org.springframework.aop.framework.ProxyFactoryBean">
<propertyname="interceptorNames"value="theAdvisor"></property>
<propertyname="target">
<reflocal="serviceImpl"/><!—目标对象-->
</property>
</bean>
4) 例子(aop & ioc)
//========================================
<!-- 声明一个业务类 -->
<bean id="baseBusiness" class="main.java.business.BaseBusiness" />
<!-- 声明自定义通知类 -->
<bean id="baseAfterReturn" class="main.java.advice.BaseAfterReturnAdvice" />
<bean id="baseAround" class="main.java.advice.BaseAroundAdvice" />
<!-- 使用ProxyFactoryBean 产生代理对象 -->
<bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
<property name="proxyInterfaces">
<value>main.java.business.IBaseBusiness</value>
</property>
<property name="target">
<ref local="baseBusiness" /> <!-- 设置目标对象 -->
</property>
<!-- 代理对象所使用的拦截器 -->
<property name="interceptorNames">
<list>
<value>baseAfterReturn</value>
<value>baseAround</value>
</list>
</property>
</bean>
//========================================
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="baseServiceBean" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
<property name="interceptorNames">
<list>
<idref local="transactionInterceptor" />
</list>
</property>
</bean>
//========================================
<!-- 定义dao -->
<bean id="xxxCommonDao" class="com.y.uuu.xxx.dao.impl.XxxCommonHibernateImpl" autowire="byName">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
<!-- 定义service实现 -->
<bean id="xxxCommonBean" class="com.y.uuu.xxx.service.impl.XxxCommonServiceImpl" autowire="byName">
<property name="baseDao">
<ref bean="xxxCommonDao" />
</property>
<property name="selfDao">
<ref bean="xxxCommonDao" />
</property>
</bean>
<!-- 定义service对外接口,此处的xxxCommonService是调用类里的成员变量,并用setter方法初始化 -->
<bean id="xxxCommonService" parent="baseServiceBean">
<property name="proxyInterfaces">
<value>com.y.uuu.xxx.service.XxxCommonService</value>
</property>
<property name="target">
<ref bean="xxxCommonBean" />
</property>
</bean>
spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。
5) 实例1
springAop.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:p="http://www.springframework.org/schema/p"
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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="byName">
<!-- ==============================利用spring自己的aop配置================================ -->
<!-- 声明一个业务类 -->
<bean id="baseBusiness" class="main.java.business.BaseBusiness" />
<!-- 声明通知类 -->
<bean id="myAroundAdvice" class="main.java.advice.BaseAroundAdvice" />
<bean id="myBeforeAdvice" class="main.java.advice.BaseBeforeAdvice" />
<bean id="myAfterReturnAdvice" class="main.java.advice.BaseAfterReturnAdvice" />
<!--
<bean id="myAfterThrowsAdvice" class="main.java.advice.BaseAfterThrowsAdvice" />
-->
<!-- 指定切点匹配类 -->
<bean id="myPointcut" class="main.java.pointcut.Pointcut" />
<!-- 包装通知,指定切点 -->
<!-- before通知器 -->
<bean id="matchBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<ref bean="myPointcut" />
</property>
<property name="advice">
<ref bean="myBeforeAdvice" />
</property>
</bean>
<!-- afterReturn通知器 -->
<bean id="matchAfterReturnAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<ref bean="myPointcut" />
</property>
<property name="advice">
<ref bean="myAfterReturnAdvice" />
</property>
</bean>
<!-- 使用ProxyFactoryBean 产生代理对象 -->
<bean id="businessProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理对象所实现的接口 ,如果有接口可以这样设置 -->
<property name="proxyInterfaces">
<value>main.java.business.IBaseBusiness</value>
</property>
<!-- 设置目标对象 -->
<property name="target">
<ref local="baseBusiness" />
</property>
<!-- 代理对象所使用的拦截器
<property name="interceptorNames">
<list>
<value>matchBeforeAdvisor</value>
<value>baseAfterReturn</value>
<value>baseAround</value>
</list>
</property>
-->
<property name="interceptorNames" value="*" />
</bean>
</beans>
main.java.pointcut.Pointcut.java
/**
* 定义一个切点,指定对应方法匹配。来供切面来针对方法进行处理<br>
*
* 继承NameMatchMethodPointcut类,来用方法名匹配
* @author
* @description:
* @tags:
*/
public class Pointcut extends NameMatchMethodPointcut {
/**
*
*/
private static final long serialVersionUID = 1370227181740818726L;
public boolean matches(Method method, Class targetClass) {
System.out.println("in pointcut*******************");
// 设置单个方法匹配
this.setMappedName("delete");
// 设置多个方法匹配
String[] methods = { "delete", "modify" };
//也可以用“ * ” 来做匹配符号
// this.setMappedName("get*");
this.setMappedNames(methods);
boolean flg = super.matches(method, targetClass);
System.out.println("flg:"+flg+"*******************");
return flg;
}
}
main.java.advice.BaseBeforeAdvice.java
/**
* 前置通知。
* @author
* @description:
* @tags:
*/
public class BaseBeforeAdvice implements MethodBeforeAdvice {
// /* (non-Javadoc)
// * @see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
// */
// public void before(Method arg0, Object[] arg1, Object arg2)
// throws Throwable {
// // TODO Auto-generated method stub
//
// }
/**
* method : 切入的方法 <br>
* args :切入方法的参数 <br>
* target :目标对象
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("===========进入beforeAdvice()============ \n");
System.out.println("准备在" + target + "对象上用" + method + "方法进行对 '" + args[0] + "'进行操作!\n");
System.out.println("要进入切入点方法了 \n");
}
}
main.java.advice.BaseAroundAdvice.java
/**
* 环绕通知
* @author
* @description:
* @tags:
*/
public class BaseAroundAdvice implements MethodInterceptor {
// public Object invoke(MethodInvocation arg0) throws Throwable {
// // TODO Auto-generated method stub
// return null;
// }
/**
* invocation :连接点
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("===========进入around环绕方法!=========== \n");
// 调用目标方法之前执行的动作
System.out.println("调用方法之前: 执行!\n");
// 调用方法的参数
Object[] args = invocation.getArguments();
// 调用的方法
Method method = invocation.getMethod();
// 获取目标对象
Object target = invocation.getThis();
// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
Object returnValue = invocation.proceed();
System.out.println("===========结束进入around环绕方法!=========== \n");
System.out.println("输出:" + args[0] + ";" + method + ";" + target + ";" + returnValue + "\n");
System.out.println("调用方法结束:之后执行!\n");
return returnValue;
}
}
main.java.advice.BaseAfterReturnAdvice.java
/**
* 后置通知
* @author
* @description:
* @tags:
*/
public class BaseAfterReturnAdvice implements AfterReturningAdvice {
// /* (non-Javadoc)
// * @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
// */
// public void afterReturning(Object arg0, Method arg1, Object[] arg2,
// Object arg3) throws Throwable {
// // TODO Auto-generated method stub
//
// }
/**
* returnValue :切入点执行完方法的返回值,但不能修改 <br>
* method :切入点方法 <br>
* args :切入点方法的参数数组 <br>
* target :目标对象
*/
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("==========进入afterReturning()=========== \n");
System.out.println("切入点方法执行完了 \n");
System.out.print(args[0] + "在"+target + "对象上被"+method + "方法删除了"+"只留下:" + returnValue + "\n\n");
}
}
main.java.advice.BaseAfterThrowsAdvice.java
/**
* 异常通知,接口没有包含任何方法。通知方法自定义
* @author
* @description:
* @tags:
*/
public class BaseAfterThrowsAdvice implements ThrowsAdvice {
/**
* 通知方法,需要按照这种格式书写
*
* @param method
* 可选:切入的方法
* @param args
* 可选:切入的方法的参数
* @param target
* 可选:目标对象
* @param throwable
* 必填 : 异常子类,出现这个异常类的子类,则会进入这个通知。
*/
public void afterThrowing(Method method, Object[] args, Object target, Throwable throwable) {
System.out.println("删除出错啦");
}
}
main.java.business.IBaseBusiness.java
/**
* 代理类接口,也是业务类接口<br>
*
* 利用接口的方式,spring aop 将默认通过jdk 动态代理来实现代理类<br>
* 不利用接口,则spring aop 将通过cglib 来实现代理类
* @author
* @description:
* @tags:
*/
public interface IBaseBusiness {
/**
* 用作代理的切入点方法
*
* @param obj
* @return
*/
public String delete(String obj);
/**
* 这方法不被切面切
*
* @param obj
* @return
*/
public String add(String obj);
/**
* 这方法切不切呢?可以设置
*
* @param obj
* @return
*/
public String modify(String obj);
}
main.java.business.BaseBusiness.java
/**
* 业务类,也是目标对象
* @author
* @description:
* @tags:
*/
public class BaseBusiness implements IBaseBusiness {
/**
* 切入点
*/
public String delete(String obj) {
System.out.println("************调用切入点:" + obj + "说:你敢删除我!************ \n");
return obj + ":瞄~";
}
public String add(String obj) {
System.out.println("************这个方法不能被切。。。************ \n");
return obj + ":瞄~ 嘿嘿!";
}
public String modify(String obj) {
System.out.println("************这个也设置加入切吧************ \n");
return obj + ":瞄改瞄啊!";
}
}
main.java.test.Debug.java
/**
* @author
* @description:
* @tags:
*/
public class Debug {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("main/java/config/springAop.xml");
IBaseBusiness business = (IBaseBusiness) context.getBean("businessProxy");
String ret = business.modify("猫");
System.out.println(">>>>>>>>>>ret:"+ret);
}
}
6) 实例2
springMVC-servlet.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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<context:annotation-config />
<!-- 通过component-scan 让Spring扫描org.swinglife.controller下的所有的类,让Spring的代码注解生效 -->
<context:component-scan base-package="com.myspringmvc.demo"></context:component-scan>
<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven/>
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 启用@AspectJ 支持
<bean class="org.springframeword.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" /> -->
<!-- 配置SpringMVC的视图渲染器, 让其前缀为:/WEB-INF/jsp/ 后缀为.jsp 将视图渲染到/WEB-INF/jsp/<method返回值>.jsp中 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 下面一行定义Spring的XML配置文件的dtd -->
<!-- "http://www.springframework.org/dtd/spring-beans.dtd" -->
<!-- 以上三行对所有的Spring配置文件都是相同的 -->
<!-- Spring配置文件的根元素 -->
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- 定义第一bean,该bean的id是chinese, class指定该bean实例的实现类 -->
<bean class="com.myspringmvc.demo.serviceImp.Chinese" id="chinese">
<!-- property元素用来指定需要容器注入的属性,axe属性需要容器注入此处是设值注入,因此Chinese类必须拥有setAxe方法 -->
<property name="axe">
<!-- 此处将另一个bean的引用注入给chinese bean -->
<ref local="stoneAxe" />
</property>
<!-- 属性的写法 -->
<!--
<property name="parm1" ref="stoneAxe" />
<property name="parm2" value="孙悟空"/>
-->
</bean>
<!-- 定义stoneAxe bean -->
<bean class="com.myspringmvc.demo.serviceImp.StoneAxe" id="stoneAxe"/>
<bean class="com.myspringmvc.demo.action.MyInterceptor" id="myInterceptor"/>
</beans>
MyInterceptor.java
/**
*
*/
package com.myspringmvc.demo.action;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* @author
* @description:
* @tags:
*/
@Aspect
public class MyInterceptor {
public MyInterceptor(){
System.out.println("===============================================");
}
//这句话是方法切入点,execution为执行的意思,*代表任意返回值,然后是包名,.*意思是包下面的所有子包或方法。(..)代表各种参数.
@Pointcut("execution(* com.myspringmvc.demo.action.*.*(..))")
private void anyMethod1(){System.out.println("---in pointcut1---");}//定义一个切入点
@Pointcut("execution(* com.myspringmvc.demo.action.*.*(..)) && args(name2)")
private void anyMethod2(String name2){System.out.println("---in pointcut2---");}//定义一个切入点
@Pointcut("execution(* com.myspringmvc.demo.action.*.*(..)) && args(name3)")
private void anyMethod3(int name3){System.out.println("---in pointcut3---");}//定义一个切入点
@Before("anyMethod1()")
public void doBefore1(){
System.out.println("前置通知1");
}
// //定义前置通知,拦截的方法不但要满足声明的切入点的条件,而且要有一个String类型的输入参数,否则不会拦截
// //args(name) 时 public void doBefore2(String name)需要有参数
// @Before("anyMethod2()")
// public void doBefore2(String name){
// System.out.println("前置通知2");
// }
// @Before(argNames="name", value="abc")
// public void doBefore3(String name){
// System.out.println(name);
// System.out.println("前置通知3");
// }
@AfterReturning("anyMethod1()")
public void doAfterReturn1(){
System.out.println("后置通知1");
}
// @AfterReturning("anyMethod2() && args(name)")
// public void doAfterReturn2(){
// System.out.println("后置通知2");
// }
// //定义后置通知,拦截的方法的返回值必须是int类型的才能拦截
// @AfterReturning(pointcut="anyMethod2()",returning="result")
// public void doAfterReturn2(int result){
// System.out.println(result);
// System.out.println("后置通知2");
// }
// @AfterReturning(argNames="result", pointcut="anyMethod()",returning="result", value="123")
// public void doAfterReturn3(int result){
// System.out.println(result);
// System.out.println("后置通知3");
// }
@After("anyMethod1()")
public void after1(){
System.out.println("最终通知1");
}
// //定义最终通知
// @After("anyMethod2() && args(name)")
// public void after2(int name){
// System.out.println("最终通知2");
// }
// @After(argNames="name", value="456")
// public void after3(int name){
// System.out.println("最终通知3");
// }
@AfterThrowing("anyMethod1()")
public void doAfterThrow1(){
System.out.println("例外通知1");
}
// @AfterThrowing("anyMethod2() && args(name)")
// public void doAfterThrow2(){
// System.out.println("例外通知2");
// }
// //定义例外通知
// @AfterThrowing(pointcut="anyMethod()",throwing="e")
// public void doAfterThrow3(Exception e){
// e.printStackTrace();
// System.out.println("例外通知3");
// }
// @AfterThrowing(argNames="e", pointcut="anyMethod()", throwing="e", value="exception")
// public void doAfterThrow4(Exception e){
// e.printStackTrace();
// System.out.println("例外通知4");
// }
@Around("anyMethod1()")
public Object doBasicProfiling1(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知1");
Object object = pjp.proceed();//当使用环绕通知时,这个方法必须调用,否则拦截到的方法就不会再执行了
System.out.println("退出环绕通知1");
return object;
}
// @Around("anyMethod2() && args(name)")
// public Object doBasicProfiling2(ProceedingJoinPoint pjp) throws Throwable{
// System.out.println("进入环绕通知2");
// Object object = pjp.proceed();//当使用环绕通知时,这个方法必须调用,否则拦截到的方法就不会再执行了
// System.out.println("退出环绕通知2");
// return object;
// }
// //定义环绕通知
// @Around(argNames="pjp", value="aaa")
// public Object doBasicProfiling2(ProceedingJoinPoint pjp) throws Throwable{
// System.out.println("进入环绕通知2");
// Object object = pjp.proceed();//当使用环绕通知时,这个方法必须调用,否则拦截到的方法就不会再执行了
// System.out.println("退出环绕通知2");
// return object;
// }
//http://ryxxlong.iteye.com/blog/719341
}
TestFirstAction.java
/**
* @Title: TestFirstAction.java
* @Package com.myspringmvc.demo.action
* @Description: TODO(TestFirstAction.java)
* @author
* @date Nov 24, 2013 5:57:33 PM
* @version V1.0
*/
package com.myspringmvc.demo.action;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.myspringmvc.demo.service.Person;
/**
* @author wujingliang
*
*/
@Controller
@RequestMapping("hello")
public class TestFirstAction{
@Resource(name="chinese") // 获取applicationContext.xml中bean的id为loginService的,并注入
protected Person chinese;
@RequestMapping(value = "/first/login.do", method = RequestMethod.GET)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response) {
System.out.println("in servlet login---chinese:"+chinese);
chinese.useAxe();
return new ModelAndView("hello/first/ok");
}
@RequestMapping(value = "/secnd/doLogin.do")
public String doLogin() {
System.out.println("in servlet doLogin---chinese:"+chinese);
chinese.useAxe();
return "end";
}
// public Person getChinese() {
// System.out.println("xx-----------");
// return chinese;
// }
//
// public void setChinese(Person chinese) {
// System.out.println("1-----------");
// System.out.println("1-----------chinese:"+chinese+", this.chinese:"+this.chinese);
// this.chinese = chinese;
// System.out.println("1-----------chinese:"+chinese+", this.chinese:"+this.chinese);
// }
// public Person getProChinese() {
// System.out.println("xx-----------");
// return proChinese;
// }
//
// public void setProChinese(Person proChinese) {
// System.out.println("1-----------");
// System.out.println("1-----------proChinese:"+proChinese+", this.proChinese:"+this.proChinese);
// this.proChinese = proChinese;
// System.out.println("1-----------proChinese:"+proChinese+", this.proChinese:"+this.proChinese);
// }
}