Spring 的设计模式以及框架结构

设计模式

         是指由专家总结出来的在某种情况下解决某类问题的最佳解决方案。是思想,是知识,抽象的

        

         设计模式的六大原则:

                   单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则、开闭原则

 

框架

         是用于处理某种问题的半成品软件,供开发人员通过定制进行高效开发.是工具

 

JavaSE的设计模式

         GOF总结了JavaSE开发中常见的23种设计模式,可以分为3大类:创建型模式、结构型模式、行为型模式

 

         JavaEE模式目前缺乏统一的说法。JavaEE设计模式有MVC模式(MVC Pattern)、数据访问对象模式DAO(Data Access Object Pattern)、

 前端控制器模式(Front Controller Pattern)       ... ...

 

接口的实现可能有多个,如何实现在多个接口具体实现之间进行切换,方法:使用工厂模式

 

工厂模式

         设计模式属于创建型模式,它提供了一种创建对象的最佳方式

         主要解决:主要解决接口实现的选择问题。

                   DAO种使用的是工厂模式的退化版本:静态工厂方法模式

        

         优点:

                   1、一个调用者想创建一个对象,只要知道其名称就可以了。

                   2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

                   3、屏蔽产品的具体实现,调用者只关心产品的接口。

         缺点:

                   每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,

                   在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

         工厂模式中产品可扩展,但是产品只能一种系列

 

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

         主要解决:提供一个创建一系列相关或相互依赖对象的接口,解决接口实现选择的问题。

        

                   public abstract class AbstractFactory {

                      public abstract Color getColor(String color);  生产产品方法1,用于生产Color接口的实现

                      public abstract Shape getShape(String shape) ;  生产产品方法2,用于生产Shape接口的实现

                   }                

                  

        

         优点:

                   当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

         缺点:

                   产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

 

产品的系列不可扩展,引入资源文件,使用反射创建字串所指代的对象

                   userDao=com.yan.dao.UserDaoImpl   Class.forName().newInstance()

         properties文件在IDE中缺乏语法检查,所以考虑采用xml作为配置

        

使用XML配置

public class BeanFactory {

                   private static final Map<String, Object> map = new HashMap<String, Object>();  容器按照id作为key,使用反射方式构建对应的对象作为value

         public static Object getBean(String key) {

                   if (map.containsKey(key))return map.get(key);  额外获取了单例的效果

                   return null;

         }

}       

         Spring框架最重要是提供了以类似上面xml+BeanFactory的方式管理配置在xml文件中的受管Bean

 

Hello Spring

         spring框架采用的是模块化架构组织代码,需要使用哪个功能则引入对应的模块,不是需要引入整个框架

                   6大模块  core  aop  数据访问和集成   web  基础  test单元测试 

                  

         Spring的IoC容器将面向接口的编程代价降到了最低

        

         1、添加jar包

                   <dependency>

                            <groupId>org.springframework</groupId>

                            <artifactId>spring-context-support</artifactId>

                            <version>5.0.8.RELEASE</version>

                   </dependency>

        

         2、定义核心配置文件,实际上文件名称没有规定,一般位于src根目录下,名称为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 id="produce" class="com.yan.test4.ProduceImpl"/>

                   </beans>

                  

         3、通过Spring获取配置的bean对象

                   Resource r = new ClassPathResource("applicationContext.xml");

                   BeanFactory fac = new XmlBeanFactory(r);

                   IProduce p=(IProduce) fac.getBean("produce");

                   System.out.println(p);    

                  

什么是Spring

         Spring是一个轻量级的控制反转IoC/DI依赖注入和面向切面AOP的开源容器框架,

                   是一个开源的Java/Java EE全功能栈full-stack的应用程序框架,以Apache许可证形式发布

        

         使用Spring框架最根本的原因实际上是声明式事务管理---EJB

 

IoC是什么 ?

         IoC -- Inversion of control, 控制反转。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则.

 

         <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">

                   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>

                   <property name="url" value="jdbc:mysql:///test"/>

                   <property name="username" value="root"/>

                   <property name="password" value="eduask"/>

         </bean>

 

         IoC有两种实现:DI依赖注入和DL依赖查找,在具体的Spring应用中一般是DI 

        

         依赖查找

                   Resource r = new ClassPathResource("applicationContext.xml");

                   // FileSystemResource("d:/abc.xml")使用的是绝对路径指定对应的核心配置文件

                   BeanFactory fac = new XmlBeanFactory(r);//定义Spring的配置可以使用properties文件和xml文件  xmlBeanFactory就是针对xml

                   Date now=(Date) fac.getBean("/now.do");

                   System.out.println(now);

 

DI是什么?

         DI -- Dependency Injection,依赖注入。即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中.

依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通

过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不用关心具体的资源来自何处、由谁实现.

         <bean id="userDao" class="com.yan.dao.UserDaoImpl">

                   <property name="ds" ref="dataSource"/>

         </bean>

 

控制反转和依赖注入之间是同一个概念吗?

    它们不是同一个概念,但是它们是同一个概念的不同角度的描述。

    控制反转(IoC)从 IoC容器的角度来说

    依赖注入(DI) 从应用程序的角度来说

 

IoC/DI的基本思想:

    1、把程序之间的依赖关系去掉

    2、把程序对象设置到IoC/DI容器的配置中,作为Bean来统一管理:<bean>--受管bean

    3、由IoC/DI容器来管理Bean的创建、实例化

    4、由IoC/DI容器来把Bean之间的关系注入到需要这些关系的对象里面

  简而言之:就是把对象之间的依赖关系全部去掉,由IoC/DI容器来管理对象和对象之间的依赖关系,实现对象之间的松散耦合.

 

IoC容器:

             简单的理解就是,实现IoC思想,并提供对象创建、对象装配以及对象生命周期管理的软件就是IoC容器

 

IoC理解:

           1、应用程序无需主动new对象;而是描述对象应该如何被创建即可,IoC容器帮你创建,即被动实例化;

           2、应用程序不需要主动装配对象之间的依赖关系,而是描述需要哪个服务,IoC容器会帮你装配,被动接受装配;

            3、主动变被动,以前创建对象、装配对象的依赖关系都是由应用程序主动完成,现在变成被动了由IoC容器来完成;

           4、应用程序不知道依赖的具体实现,只知道需要提供某类服务的对象,达到并松散耦合;

           5、是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则.

 

使用Ioc/DI容器开发需要改变思路:

           1、应用程序不主动创建对象,但是要描述创建它们的方法

                   不要滥用依赖注入

  2、在应用程序代码中不直接进行服务的装配,但是要描述哪一个组件需要哪一项服务,由容器负责将这些装配在一起

  也就是说:所有的组件都是被动的,组件初始化和装配都由容器负责,应用程序只是获取相应的组件后,实现应用的功能即可.

 

定义对应的类,并进行配置,将对象纳入到容器中进行管理(受管bean)

    【(Spring2.5-)xml配置   xml+注解配置  (Spring4.0+建议)JavaConfig类配置】

 

         xml配置的方法:

                   <bean id="受管bean的id名称,不允许重复" name="多个名称,名称之间使用逗号或者空格分隔" class="受管bean类的全名"/>

                            xml的配置泥潭问题                        

                  

         xml+注解的方式:    [重点]

                   Spring引入了配置文件中的context名空间以简化配置

                            xmlns:context="http://www.springframework.org/schema/context"

                            xsi:schemaLocation中添加http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd

                  

                   打开自动组件扫描        

                            <context:component-scan base-package="com.yan"/>使Spring框架在启动时自动扫描com.yan包及其子包,如果发现类上有对应的注解,则自动添加为受管bean

                           

                            注解:

                                     @Component声明当前类是一个组件,是一个受管bean

                                               @Controller声明控制器,@Service声明业务bean,@Repository声明DAO

                                              

                                               @Target({ElementType.TYPE})用于在类上进行使用

                                               @Retention(RetentionPolicy.RUNTIME)保留到运行时

                                               public @interface Repository {

                                                        @AliasFor(annotation = Component.class)别名定义

                                                        String value() default "";

                                               }

                  

                                     使用属性value="受管bean的名称,类似于xml中的id",默认名称就是类名称,只是首字母小写

                                               UserDaoImpl userDao=(UserDaoImpl) fac.getBean("userDaoImpl");

                                              

                                     定义时按照注解的语法规定,如果只有一个属性,而且名称为value时,value=可以不写

                           

                            在类上添加对应的注解,声明受管bean   

                                               @Component("userDao")

                                               public class UserDaoImpl {

                                              

                                               }

        

         JavaConfig的定义方法:  实现了0配置   [要求--Springboot]

        

                   定义配置类

                            @Configuration  //声明当前类是一个配置类,类中都是配置信息

                   public class MyConfig {

                            @Bean//用于声明方法的返回值是一个受管bean对象,这里可以添加(value="bbb")定义受管bean的名称,如果不定义则方法名称就是受管bean的名称

                            public UserDaoImpl userDao(){

                                     return new UserDaoImpl();

                            }

                   }                                            

                  

                   如果获取受管bean

                            BeanFactory fac = new AnnotationConfigApplicationContext(MyConfig.class);

                            UserDaoImpl userDao=(UserDaoImpl) fac.getBean("userDao");          

                           

获取受管bean的方法:BeanFactory和ApplicationContext接口

         ApplicationContext和BeanFactory可以引用Spring的IoC/DI容器,ApplicationContext接口是BeanFactory接口的子接口

                   获取BeanFactory的方法:

                            Resource r = new ClassPathResource("applicationContext.xml");  FileSytemResource

                            BeanFactory fac = new XmlBeanFactory(r);

                   获取applicationContext的方法:

                            ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

                                     FileSystemXmlApplicationContext

        

         BeanFacotry是spring中比较原始的Factory,提供了IoC/DI容器使用的最基本的方法。

                   Object getBean(String name) throws BeansException;按照名称查找对应的受管bean   <bean>id和name属性

                            Date dd=(Date)fac.getBean("now");

                   <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;按照名称查找对应的受管bean,同时自动对返回值进行类型转换(这种方式不需要进行类型转换

                            Date dd=fac.getBean("now",Date.class);

                  

                   <T> T getBean(Class<T> requiredType) throws BeansException;按照类型查找受管bean,注意不能有2个同类型的受管bean(有且只有唯一的一个)

                            Date dd=fac.getBean(Date.class);其中参数可以是具体类,也可以是抽象的类型

                           

         ApplicationContext接口由BeanFactory接口派生而来,在提供BeanFactory所有的功能基础上提供了企业级应用开发的支持,例如读取资源文件、发布事件等。

 

         BeanFactory针对单例的受管bean采用的是延迟加载(费内存,速度快),ApplicationContext采用的是立即加载(不费内存,速度)【所有的prototype受管bean都是延迟】

        

         所以BeanFactory一般用于内存敏感的受限环境开发中,ApplicationContext一般使用

        

受管bean的scope声明范围:

                   5个值:  默认singleton单例模式,表示这个受管bean是单例的,获取100次也是同一个对象

                                prototype原型模式,表示获取一次新建一次,这个受管bean是多实例的,获取100次则得到100个不同的对象

                               

                                另外还有和web应用相关的3个配置值:

                                         request  session   global session

                  

         <bean id="now" class="java.util.Date" scope="prototype"/>       

 

 

Spring框架4大原则:

         使用POJO进行轻量级和最小侵入式开发

                  POJOPlain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。

         通过控制反转IoC、依赖注入DI和基于接口编程实现松耦合

         通过AOP和默认惯例进行声明式编程

         使用AOP和模板编程减少模式化代码。

 

Spring框架的主要功能

         基于Java Beans的配置管理,采用IOC的原理,特别是对依赖注射技术的使用。这些都用来减少各组件间对实施细则的相互依赖性。

         一个核心的,全局适用的bean工厂

         一个一般抽象化的层面来管理数据库间的数据处理

         建立在框架内的,对Java数据处理API和单独的JDBC数据源的一般性策略。因此,在数据处理支持上对Java企业版本环境的依赖性得以消除

         和一些整合持久化的框架,如Hibernate,JDO,iBATIS和db4o,的整合

         web应用中的MVC框架,基于核心的Spring功能,支持多种产生视图的技术,包括JSP,FreeMarker,Velocity,Tiles,iText,和POI

         大量的AOP框架以提供诸如数据处理管理的服务。同IOC的功能一样,目的是提高系统的模块化程度

 

基本框架结构[过去的面试,难度系数1]

         Spring是一种JavaEE开发中的一站式解决方案,所以其中包含的内容比较多,为了避免在应用中添加无用功能,所以Spring采用了非强制性的模块化结构,在具体应用中,可以根据应用所需要的功能进行选择添加。

Spring3.x分为6大模块,Spring的模块化是很强的,各个功能模块都是独立的,可以选择的使用

 

         Core模块是Spring应用的基础,提供了最基本的IoC/DI容器的支持和实现

         AOP模块建立在Core模块的基础上,提供了AOP技术的支持,以简化AOP编程的复杂度

         DAO和ORM模块建立在AOP的基础上,DAO提供了dao模式编程的简化支持,ORM提供了整合持久层框架的支持。同时在AOP的基础上给数据库访问提供了声明式事务的支持。

         JEE模块建立在Core模块的基础上,提供了针对EJB\邮件javaMail等企业级应用支持

         Web模块建立在core模块的基础上,提供了整合其它表现层框架【例如Struts\JSF等】的支持,同时提出了自己的表现层框架SpringMVC[针对Struts1提出的]

 

Spring4.x分为6大模块,包括20多个子项目

 

 

Spring的全方位应用程序框架

         SSH2:表现层Struts2业务层Spring持久层hibernate  --一般的大型开发中还有使用

         SSM:表现层SpringMVC业务层Spring持久MyBatis---主流,在互联网应用开发中主要使用

 

受管bean

         Spring中那些组成应用的主体以及由Spring IoC容器所管理的对象被称之为beanBean就是由Spring容器初始化、装配以及被管理的对象

         bean是Spring管理的基本单位,在Spring的应用中,所有的组件都是beanbean包括数据源、 HibernateSessionFactory及事务管理器等。 Spring里的bean是非常广义的概念,任何的Java对象,Java组件都可被当成bean处理。甚至这些组件并不是标准的JavaBean

<bean>配置一个 bean

    id为在代码中引用的名称,唯一,且可以为多个,利用逗号、分号、或空格分隔

    class指向全类名,spring就是根据全类名,通过反射生成一个实例对象,id为这个实例对象的引用,所以必须在这个类中添加默认的构造方法,并且属性要有setter方法

    scope=singleton/prototype   request/session/global session

 

 

给受管bean配置属性

         例如:定义连接池(c3p0),需要配置驱动串、连接串等参数

                   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

                            <!-- 使用property配置属性(set方法),

value用于定义对应的简单数据(8种原生类型及其包装类\String(传值用value)

ref用于引用其它的受管bean (传类型用ref-->

                            <property name="driverClass" value="com.mysql.jdbc.Driver"/>

                            <property name="jdbcUrl" value="jdbc:mysql:///test"/>

                            <property name="user">

                                     <value>root</value>等价于<property name="" value=""/>,如果传递的是另外的受管bean,可以使用<ref/>

                            </property>

                            <property name="password" value="eduask"/>

                   </bean>

                  

                   更换数据库平台只需要修改配置即可,这种方式和具体的数据库平台不耦合

        

         例如UserDao的执行需要使用连接池

                   public class UserDao {

                            //      private ComboPooledDataSource ds;//耦合具体的连接池

                                     private DataSource ds;//使用接口声明,可以得到松耦合的目的,具体的连接池产品可以任意更换

                                     public void show() throws SQLException{

                                               System.out.println(ds.getConnection());

                                     }       

                  

                   <bean id="userDao" class="com.yan.test3.UserDao">

                            <property name="ds" ref="dataSource"/>

                   </bean>

                    

                   <bean id="userDao" class="com.yan.test3.UserDao">

                            <property name="ds">

                                     <ref bean="dataSource"/>

                            </property>

                   </bean>

                  

        

         Spring为了简化配置,引入p名空间   [建议]

                            <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"

                                     p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql:///test"

                                     p:user="root" p:password="eduask"/>

                            <bean id="userDao" class="com.yan.test3.UserDao" p:ds-ref="dataSource"/>

                           

         注解的定义方式:引入SpEL简化注解配置

                   @autowired自动装配,默认是按照类型自动装配            

                  

 

依赖注入DI通常有三种方法:接口注入、设置注入和构造器注入[Spring工厂注入]

 

         设置器注入:属性注入

                   属性注入即通过setter方法注入Bean的属性值或依赖的对象,使用元素<property>使用name属性指定Bean类的属性名称,value属性或 子节点指定属性值。

                   【在这里,注入是根据setter方法来的而不是属性名称,但是一般我们都是直接生成setter方法,而不去更改这个名称,所以就是说成name为属性名称】

                  注入简单数据,使用value属性

注入复杂数据,使用ref属性

                  

                   要求:无参构造器[private也可以],否则BeanCreationException

                              建议:私有的属性,共有的get/set方法

        

         构造方法注入:

                   通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。

                   构造器注入在 【constructor-arg】 元素里声明属性,

        

                   受管bean中包含一个构造器

                            public class UserDao {

                                     private DataSource ds;

                                     public UserDao(DataSource ds) {

                                               this.ds = ds;

                                     }       

                           

                   配置

                            <bean id="userDao" class="com.yan.test3.UserDao">

                                      <constructor-arg ref="dataSource"/>   要求对应的构造器

                            </bean>

                           

                   如果构造器为

                            public UserDao(String name,int num,DataSource ds) {

                                     System.out.println(name+"---"+num);

                                     this.ds = ds;

                            }

                           

                            <bean id="userDao" class="com.yan.test3.UserDao">

                                     <constructor-arg ref="dataSource"/>这个不会有2意性,但是后面的两个配置则有2意的问题

                                     <constructor-arg value="yanjun"/>

                                     <constructor-arg value="12"/>

                            </bean>  

                           

                            如果配置为

                                     <bean id="userDao" class="com.yan.test3.UserDao">

                                               <constructor-arg value="yanjun"/>

                                               <constructor-arg value="12"/>

                                               <constructor-arg ref="dataSource"/>

                                     </bean>没有问题,默认配置的顺序就是参数的顺序,只有顺序不对时才进行类型的判断

                                    

                            一般来说,按照顺序一个一个的进行配置,也就是配置的顺序和构造器参数的顺序一致。如果需要打乱顺序,建议添加序号或者类型说明

                            <bean id="userDao" class="com.yan.test3.UserDao">

                                     <constructor-arg value="12" index="1"/>

                                     <constructor-arg ref="dataSource"/>

                                     <constructor-arg value="yanjun"/>

                            </bean>  

                           

                            <bean id="userDao" class="com.yan.test3.UserDao">

                                     <constructor-arg value="12" type="int"/>

                                     <constructor-arg ref="dataSource"/>

                                     <constructor-arg value="yanjun" type="java.lang.String"/>

                            </bean>  

                           

         如何选用?

                   理论上来说应该采用构造器注入,因为可以在对象的创建阶段创建一个完整可用的对象,比较符合面向对象的思想;但是具体开发中一般使用设置器注入较多,

         因为这种方式比较灵活                  

 

Spring针对junit的单元测试进行了扩展支持

         1、添加依赖

                   <dependency>

                            <groupId>org.springframework</groupId>

                            <artifactId>spring-test</artifactId>

                            <version>5.0.8.RELEASE</version>

                            <scope>test</scope>

                   </dependency>

                   <dependency>

                            <groupId>junit</groupId>

                            <artifactId>junit</artifactId>

                            <version>4.12</version>

                            <scope>test</scope>

                   </dependency>

                  

         2、定义单元测试用例类

                   @RunWith(SpringJUnit4ClassRunner.class)  设置执行测试的运行器

                   @ContextConfiguration(locations="classpath:applicationContext.xml")定义配置文件的位置

                   public class DataSourceTest {

                            @Autowired  自动注入,不需要再手动获取

                            private DataSource ds;  在核心配置文件中定义的受管bean

                            @Test

                            public void testConnection() throws SQLException {

                                     System.out.println(ds.getConnection());

                            }

                   }       

 

注解分为两类:

1、一类是使用Bean,即是把已经在xml文件中配置好的Bean拿来用,完成属性、方法的组装;比如@Autowired , @Resource,可以通过byTYPE(@Autowired)、byNAME(@Resource)的方式获取Bean;

 

2、一类是注册Bean,@Component , @Repository , @ Controller , @Service , @Configration这些注解都是把你要实例化的对象转化成一个Bean,放在IoC容器中,等你要用的时候,它会和上面的@Autowired , @Resource配合到一起,把对象、属性、方法完美组装。

 

POJOPlain Ordinary Java Object)即普通Java类,具有一部分get/set方法的那种类就可以称作POJO

实际意义就是普通的JavaBeans(简单的实体类),特点就是支持业务逻辑的协助类。
POJO类的作用是方便程序员使用数据库中的数据表,对于程序员来说,可以很方便的将POJO类当作对象来进行使用,也可以方便的调用其getset方法。
但不允许有业务方法,也不能携带有connection之类的方法,即不包含业务逻辑或持久逻辑等。


按着Sun公司的定义,JavaBean是一个可重复使用的软件组件。实际上JavaBean是一种Java类,通过封装属性和方法成为具有某种功能或者处理某个业务的对象,简称bean
JavaBean 是一种JAVA语言写成的可重用组件。它的方法命名,构造及行为必须符合特定的约定:
这个类必须有一个公共的缺省构造函数。
这个类的属性使用gettersetter来访问,其他方法遵从标准命名规范。
这个类应是可序列化的。 
因为这些要求主要是靠约定而不是靠实现接口,所以许多开发者把JavaBean看作遵从特定命名约定的POJO

JavaBean的任务就是: “Write once, run anywhere, reuse everywhere”,即一次性编写,任何地方执行,任何地方重用
JavaBean可分为两种:一种是有用户界面(UIUser Interface)的JavaBean;还有一种是没有用户界面,主要负责处理事务(如数据运算,操纵数据库)的JavaBeanJSP通常访问的是后一种JavaBean

简而言之,当一个Pojo可序列化,有一个无参的构造函数,使用gettersetter方法来访问属性时,他就是一个JavaBean

猜你喜欢

转载自blog.csdn.net/java_laoming/article/details/81329115