精华内容
下载资源
问答
  • 常用三大java框架

    万次阅读 2018-06-14 09:55:40
    1.Structs框架是最早的Java开源框架之一.Struts是MVC设计模式的一个优秀实现.Struts是最早的java开源框架之一,它是MVC设计模式的一个优秀实现。 Struts定义了通用的Controller(控制器),通过配置文件(通常是 ...


    MVC开发模式

     显示层(客户端层)控制层(Web层)业务层数据层(持久层)
    作用1.负责数据页面显示   
    2.联合HTML,CSS,JavaScript
    1.服务器端验证,
    2.数据接收
    4.POJO(简单的Java类即普通的javaBean)转换
    3.调用业务层
    4.进行显示层跳转
    1.负责数据库打开与关闭
    2.调用数据层并整理数据
    3.处理事务
    1.只负责数据的CRUD(增.删.改.查)
    维护麻烦JSP+EL+JSTL+AJAX+
    JavaScript+DOM
    Servletjava原生编码JDBC原生编码
     jQuery+JSON
    (简化数据传输,
    隐藏DOM操作)
    +Bootstrap(前台框架,
    简化DIV+CSS)
    Struts2
    或者
    Spring-MVC
    SpringHibernate(提升数据库操作性能和简化代码)


    1.Structs框架是最早的Java开源框架之一.Struts是MVC设计模式的一个优秀实现.

    Struts是最早的java开源框架之一,它是MVC设计模式的一个优秀实现。 Struts定义了通用的Controller(控制器),通过配置文件(通常是 Struts -config.xml)隔离Model(模型)和View(视图),以Action的概念以对用户请求进行了封装,使代码更加清晰易读。 Struts还提供了自动将请求的数据填充到对象中以及页面标签等简化编码的工具。 Struts能够开发大型Java Web项目。

    2.Struts2以WebWork优秀的设计思想为核心,吸收了 Struts框架的部分优点,提供了一个更加整洁的MVC设计模式实现的Web 应用程序框架

    Struts2以WebWork优秀的设计思想为核心,吸收了 Struts框架的部分优点,提供了一个更加整洁的MVC设计模式实现的Web 应用程序框架。 Struts2引入了几个新的框架特性:从逻辑中分离出横切关注点的拦截器、减少或者消除配置文件、贯穿整个框架的强大表达式语言、支持可变更和可重用的基于MVC模式的标签API, Struts2充分利用了从其它MVC框架学到的经验和教训,使得 Struts2框架更加清晰灵活。

    3.Hibernate是一个开放源代码的对象关系映射框架

    它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

    4.Spring框架是由于软件开发的复杂性而创建的。

    Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。


    展开全文
  • Java常用三大框架    —— Hibernate 框架     项目的三层结构    一个完整的WEB项目,一般情况下都分为三层结构:WEB层、业务层、持久层。    WEB层:与客户端进行数据交互。客户端发送请求到WEB...

     

    Java常用三大框架

     

            —— Hibernate 框架

     

     

    项目的三层结构

     

                    一个完整的WEB项目,一般情况下都分为三层结构:WEB层、业务层、持久层。

     

                    WEB层:与客户端进行数据交互。客户端发送请求到WEB层, WEB层接收并简单的封装后传递给业务层。

     

                    业务层 :处理业务逻辑。将处理过后的数据交给持久层。

     

                    持久层 :负责与底层数据交互。主要体现在与数据库的交互。

     

     

     

    JAVA三大框架简单介绍

     

     

     

                    JAVA 最常用最基础的两套框架分别是 SSH 和 SSM 框架。

                    本次主要学习 JAVA 的 SSH 三大框架。

                    SSH 三大框架是指:Struct2 + Spring + Hibernate。

     

                    Struct2 : 开源框架。主要体现在 WEB 层。当服务器启动后,根据配置文件加载服务到内存。

     

                                      提供了数据实体(JAVABean)、视图(View)、控制器(Controller)的相关组件。

     

                    Spring:    主要体现在业务层。

     

                                      提供了管理业务对象的一致方法,鼓励使用接口式编程。还提供了唯一的数据管理,包括JDBC框架。

     

                    Hibernate:开源的对象关系映射框架。主要体现在持久层。

     

                                      对JDBC进行了轻量级的对象封装,更加方便的操作数据库。

     

    Hibernate 框架介绍

                    Hibernate 是一个开源的对象关系映射框架。对JDBC进行了轻量级的对象封装,将POJO与数据库表建立了映射关系。是一个全自动的ORM(Object Relational Mapping:对象关系映射)框架。可以自动生成SQL语句,自动执行SQL语句,使得程序员可以方便的使用操作对象的思维操作数据库。使用Hibernate框架主要用于完成数据的持久化。

     

                    优点

     

                            1、对JDBC访问数据库的代码做了封装,极大的简化了数据访问层繁琐的重复性代码;

     

                            2、基于JDBC的主流持久化框架,是一个非常优秀的ORM实现,很大程度的简化了DAO层的编码工作;

     

     

                            3、性能非常好,因为它是一个轻量级框架,映射的灵活性非常出色,且支持很多关系型数据库,从一对一到一对多的各种复杂关系。

     

     

     

    Hibernate 框架开发入门

     

     

    Step 1 :下载Hibernate运行环境

     

            https://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/hibernate-release-5.0.7.Final.zip/download  点击打开链接

     

     

     

    Step 2: 创建数据表结构(以MySQL数据库为例)

    CREATE DATABASE db_hibernate_test;
    USE db_hibernate_test;
    CREATE TABLE `t_customer` (
      `cust_id` BIGINT(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
      `cust_name` VARCHAR(32) NOT NULL COMMENT '客户名称(公司名称)',
      `cust_user_id` BIGINT(32) DEFAULT NULL COMMENT '负责人id',
      `cust_create_id` BIGINT(32) DEFAULT NULL COMMENT '创建人id',
      `cust_source` VARCHAR(32) DEFAULT NULL COMMENT '客户信息来源',
      `cust_industry` VARCHAR(32) DEFAULT NULL COMMENT '客户所属行业',
      `cust_level` VARCHAR(32) DEFAULT NULL COMMENT '客户级别',
      `cust_linkman` VARCHAR(64) DEFAULT NULL COMMENT '联系人',
      `cust_phone` VARCHAR(64) DEFAULT NULL COMMENT '固定电话',
      `cust_mobile` VARCHAR(16) DEFAULT NULL COMMENT '移动电话',
      PRIMARY KEY (`cust_id`)
    ) 
    ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

     

     

     

    Step 3: 搭建Hibernate开发环境

     

        1、安装JDK + Myeclipse开发环境 + Tomcat

        2、启动MyEclipse,新建Web项目。

     

            

     

      3、导入MySQL驱动包 + Hibernate 驱动包 + 日志包

     

     

            

     

    Step 4:编写JavaBean实体类

            

    package com.mcd.domain;
    /**
     * 客户的JavaBean实体类
     * @author 
     */
    public class Customer {
    	
    	private Long cust_id;	//建议使用包装类型,包装类型的默认值是null, 而基本类型的默认值是 0。
    	private String cust_name;
    	private Long cust_user_id;
    	private Long cust_create_id;
    	private String cust_source;
    	private String cust_industry;
    	private String cust_level;
    	private String cust_linkman;
    	private String cust_phone;
    	private String cust_mobile;
    	// ...  Set/Get方法自动生成 这里省略
    }
    

    Step 5:创建类与表结构之间的映射

     

       1、建议映射的配置文件与JavaBean放置在同一目录;

     

       2、建议映射文件名称为 JavaBean的名字 + .hbm.xml;    (hbm:意为 :Hibernate Map)

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
    	<!-- 配置类和表结构的映射 -->
    	<class name="com.mcd.domain.Customer" table="t_customer">
    		<!-- 配置id 
    			name属性,JavaBean的属性
    			column属性,是表结构的字段
    		-->
    		<id name="cust_id" column="cust_id">
    			<!-- 主键的生成策略 -->
    			<generator class="native"/>
    		</id>	
    		<!-- 配置其他的属性 -->
    		<property name="cust_name" column="cust_name"/>
    		<property name="cust_user_id" column="cust_user_id"/>
    		<property name="cust_create_id" column="cust_create_id"/>
    		<property name="cust_source" column="cust_source"/>
    		<property name="cust_industry" column="cust_industry"/>
    		<property name="cust_level" column="cust_level"/>
    		<property name="cust_linkman" column="cust_linkman"/>
    		<property name="cust_phone" column="cust_phone"/>
    		<property name="cust_mobile" column="cust_mobile"/>		
    	</class>	
    </hibernate-mapping>   

    Step 6 : 创建Hibernate核心配置文件

     

       核心配置文件整个项目只需要写一个。   

     

       1、核心配置文件必须放在src的根目录下;

     

       2、核心配置文件名必须为:hibernate.cfg.xml;      (cfg:意为:configuration)

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
    	<!-- 先配置SessionFactory标签,一个数据库对应一个SessionFactory标签 -->
    	<session-factory>
    		<!-- 必须要配置的参数有5个,4个连接参数(驱动 + 数据库名 + 用户名 + 密码) + 数据库的方言参数 -->
    		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    		<property name="hibernate.connection.url">jdbc:mysql:///db_hibernate_test</property>
    		<property name="hibernate.connection.username">root</property>
    		<property name="hibernate.connection.password">root</property>
    		<!-- 数据库的方言 -->
    		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    
    		<!-- 可选配置 -->
    		<!-- 显示SQL语句,在控制台显示 -->
    		<property name="hibernate.show_sql">true</property>
    		<!-- 格式化SQL语句 -->
    		<property name="hibernate.format_sql">true</property>
    		<!-- 生成数据库的表结构 
    			update:如果没有表结构,创建表结构。如果存在,不会创建,添加数据
    		-->
    		<property name="hibernate.hbm2ddl.auto">update</property>
    		<!-- 映射配置文件,需要引入映射的配置文件 -->
    		<mapping resource="com/mcd/domain/Customer.hbm.xml"/>
    	</session-factory>
    </hibernate-configuration>	

    Step 7 :测试一下框架搭建是否正常

     

       1、创建测试包,导入单元测试的Jar, 创建单元测试

    package com.mcd.test;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.junit.Test;
    import com.mcd.domain.Customer;
    
    /**
     * 测试 Hibernate 框架
     * @author 
     *
     */
    public class TestDemo1 {
    
    	/**
    	 * 单元测试  测试保存客户功能
    	 */
    	@Test
    	public void testSave(){
    		
    		//1、加载配置文件
    		Configuration config = new Configuration();	
    		//默认加载src目录下 hibernate.cfg.xml 配置文件
    		config.configure();
    		
    		//2、创建SessionFactory对象, 生成Session对象
    		SessionFactory factory = config.buildSessionFactory();
    		
    		//3、创建Session对象
    		Session session = factory.openSession();
    		
    		//4、开启事务
    		Transaction tr = session.beginTransaction();
    		
    		//5、编写保存客户的代码
    		Customer c = new Customer();
    //		c.setCust_id(cust_id);		主键 由框架自动管理 且自动递增 这里不需要手动设置
    		c.setCust_name("测试用户");
    		c.setCust_user_id(1000000);
    		c.setCust_create_id(1680502);
    		c.setCust_industry("系统开发");
    		c.setCust_level("A");
    		c.setCust_linkman("MCD");
    		c.setCust_mobile("188********");
    		c.setCust_phone("496203");
    		c.setCust_source("");
    		//保存数据 操作对象就相当于操作数据库表的结构
    		session.save(c);
    		
    		//6、提交事务
    		tr.commit();
    		
    		//7、释放资源
    		session.close();
    		factory.close();
    	}
    }
    

       2、Run AS "JUint Test" 运行单元测试, 控制台会有以下提示

    log4j:WARN No appenders could be found for logger (org.jboss.logging).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    Hibernate: 
        insert 
        into
            t_customer
            (cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?)
    

    同时, 数据库中多出一条数据

     

     

    说明框架搭建没有问题。

     

    step 8 :Session对象的CRUD操作

     

      1、 为了方便操作,先写一个工具类

    package com.mcd.utils;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    /**
     * Hibernate 框架 的工具类
     */
    
    public class HibernateUtils {
    
    	private static final Configuration CONFIG;
    	private static final SessionFactory FACTORY;
    	
    	static {
    		
    		//加载XML的配置文件
    		CONFIG = new Configuration().configure();
    		
    		//构造工厂
    		FACTORY = CONFIG.buildSessionFactory();
    	}
    	
    	/**
    	 * 从工厂中获取Session对象
    	 * @return
    	 */
    	public static Session getSession() {
    		return FACTORY.openSession();
    	}
    }
    

     

       2、测试 Session 对象的 CRUD 操作(TestDemo1 类中)

            /**
    	 * 单元测试 3 测试Session对象的CRUD
    	 */
    	@Test
    	public void testSave3(){
    		Session session = HibernateUtils.getSession();
    		Transaction tr = session.beginTransaction();
    		
    		//查询 :第一个参数是JavaBean的反射, 第二个参数是需要查询的数据的 主键 (因为我们定义的主键是Long类型的,所以加L后缀)
    		Customer c = session.get(Customer.class, 1L);
    		System.out.println(c);
    		
    		//修改:参数为对象
    		//★ 修改 删除 都建议先查询 后进行改删操作
    		c.setCust_create_id(1234567);
    		c.setCust_phone("7654321");
    		session.update(c);
    		
    		//删除:参数为对象
    		session.delete(c);
                    
                    //新增或修改:如果存在则修改,如果不存在则新增
                    c.setCust_create_id(987);    //★新增:主键由Hibernate自动处理,切记不要操作主键
                    session.saveOrUpdate(c);
    
    		tr.commit();
    		session.close();
    	}

      3、控制台输出内容

    log4j:WARN No appenders could be found for logger (org.jboss.logging).
    log4j:WARN Please initialize the log4j system properly.
    log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
    Hibernate: 
        select
            customer0_.cust_id as cust_id1_0_0_,
            customer0_.cust_name as cust_nam2_0_0_,
            customer0_.cust_user_id as cust_use3_0_0_,
            customer0_.cust_create_id as cust_cre4_0_0_,
            customer0_.cust_source as cust_sou5_0_0_,
            customer0_.cust_industry as cust_ind6_0_0_,
            customer0_.cust_level as cust_lev7_0_0_,
            customer0_.cust_linkman as cust_lin8_0_0_,
            customer0_.cust_phone as cust_pho9_0_0_,
            customer0_.cust_mobile as cust_mo10_0_0_ 
        from
            t_customer customer0_ 
        where
            customer0_.cust_id=?
    Customer [cust_id=1, cust_name=测试用户, cust_user_id=1000000, cust_create_id=1680502, cust_source=, cust_industry=系统开发, cust_level=A, cust_linkman=MCD, cust_phone=496203, cust_mobile=188********]
    Hibernate: 
        delete 
        from
            t_customer 
        where
            cust_id=?
    Hibernate: 
        insert 
        into
            t_customer
            (cust_name, cust_user_id, cust_create_id, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile) 
        values
            (?, ?, ?, ?, ?, ?, ?, ?, ?)
    

       4、刚才查询到的数据已经被删除

     

    step 9 : Session 对象的综合查询

        @Test
    	public void testSave4(){
    		
    		Session session = HibernateUtils.getSession();
    		Transaction tr =  session.beginTransaction();
    		
    		//创建查询接口
    		Query query = session.createQuery("from Customer");
    		//查询所有数据 select * from Customer
    		List<Customer> list = query.list();
    		for (Customer item : list) {
    			System.out.println(item);
    		}
    		
    		tr.commit();//事务的提交方法
    		// tr.rollback();  // 事务的回滚方法 -- 一般写在 catch 语句中
    		session.close();
    	}

     

    展开全文
  • 先说说三大框架整合过程 。个人认为使用框架并不是很难,关键要理解其思想,这对于我们提高编程水平很有帮助。不过,如果用都不会,谈思想就变成纸上谈兵了!!! 先技术,再思想。实践出真知。 SSM(Spring+Spring ...

    先说说三大框架整合过程 。个人认为使用框架并不是很难,关键要理解其思想,这对于我们提高编程水平很有帮助。不过,如果用都不会,谈思想就变成纸上谈兵了!!! 先技术,再思想。实践出真知。 SSM(Spring+Spring MVC+Mybatis),目前较为主流的企业级架构方案。标准的MVC设计模式,将整个系统划分为显示层、Controller层、Service层、Dao层四层,使用Spring MVC负责请求的转发和视图管理,Spring实现业务对象管理, MyBatis作为数据对象持久化引擎。

    三大框架实战文档

    Spring是一个轻量级的Java开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

    2b63161c6224ad0ed3a46c4df1f539e7.png

    Spring MVC属于SpringFrameWork的后续产品,分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。

    44a8e1b143d2ab6a03c0dc21a62de8f4.png

    MyBatis是一个基于Java的持久层框架。MyBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)它消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java映射成数据库中的记录

    8c0fb9512cb712787ee448b457af810c.png

    SSM实战文档领取

    3718222f1442d039f20c3be4387830e4.png

    获取方式:转发+关注然后私信“架构”即可免费获取

    展开全文
  • java三大框架

    热门讨论 2012-01-19 09:21:04
    框架在一起并不冲突,所以现在最常用框架就是 struts+hibernate+spring就像我们盖房子一样,先把框架搭好,我们在在上面写代码就很规范。 Struts框架介绍 : Struts只是一个MVC框架(Framework),用于...
  • 盘点Java框架常用的3底层技术! 这段时间很久没有更新自己的博客了,对自己的不严谨和不自律批评下自己,以后更加严格要求自己。 这篇博客像往常一样主要复习底层知识点,本文所介绍的个Java底层技术,有着逐渐...

    盘点Java框架常用的3大底层技术!

    这段时间很久没有更新自己的博客了,对自己的不严谨和不自律批评下自己,以后更加严格要求自己。

    这篇博客像往常一样主要复习底层知识点,本文所介绍的三个Java底层技术,有着逐渐递进的特点,Java注解中使用了JDK动态代理,而JDK动态代理中运用了Java反射。

    同样的是通过架构师这个微信公众号来学习的。

    Java注解

    当我们阅读框架源码时,会看到其中包含着大量的注解,注解被广泛使用的原因在于,可以生成一些通用的“模板化”代码,来避免重复性的工作。使用注解的工作模式是,通过注解来描述我们的意图,然后用注解解析工具对注解进行解析。

    实验:自定义注解

    首先我们通过@interface关键字定义一个注解@Tree,定义注解时,需要定义两个内容:元注解,注解属性。

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Tree {
    
        String name() default "tree";
    }
    

    元注解:

    可以看到我在上面还添加了@Target@Retention,这个是元注解,也就是添加到注解之上的注解,元注解有5种:

    @Retention:声明注解的的存活时间

    RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

    RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

    RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

    @Target:声明注解运用的地方

    ElementType.ANNOTATION_TYPE 应用到注解

    ElementType.CONSTRUCTOR 应用到构造方法

    ElementType.FIELD 应用到属性

    ElementType.LOCAL_VARIABLE 应用到局部变量

    ElementType.METHOD 应用到方法

    ElementType.PACKAGE 应用到包

    ElementType.PARAMETER 应用到方法内的参数

    ElementType.TYPE 应用到类型(类、接口、枚举)

    @Documented:将注解中的元素包含到 Javadoc 中
    @Inherited:使用了这个注解的子类,就继承了该注解
    @Repeatable:Java1.8新增特性,应用于注解的值可以取多个的场景

    注解属性:

    可以类比为普通类中的成员变量,注解中只有成员变量没有方法,在使用该注解时为该属性赋值,也可以在定义时赋默认值。

    【注解处理器】

    在注解处理器中,我们可以为注解定义逻辑,例如在下面的例子中,就是调用AnnotationClient类中所有方法,把@Tree中name属性值注入到方法中。

    public class TreeProcessor {
        public void parseMethod(final Class<?> clazz) throws Exception {
            final Object obj = clazz.getConstructor(new Class[] {}).newInstance(new Object[] {});
            final Method[] methods = clazz.getDeclaredMethods();
            for (final Method method : methods) {
                final Tree my = method.getAnnotation(Tree.class);
                if (null != my) {
                    method.invoke(obj, my.name());
                }
            }
        }
    }
    

    接下来做一下测试:

    public class AnnotationClient {
    
        @Tree
        public static void sayHello(final String name) {
            System.out.println("==>> Hi, " + name + " [sayHello]");
        }
    
        @Tree(name = "Someone")
        public static void sayHelloToSomeone(final String name) {
            System.out.println("==>> Hi, " + name + " [sayHelloToSomeone]");
        }
    
        public static void main(final String[] args) throws Exception {
            final TreeProcessor treeProcessor = new TreeProcessor();
            treeProcessor.parseMethod(AnnotationClient.class);
        }
    
    }
    

    实现:

    ==>> Hi, Someone [syaHelloToSomeone]
    ==>> Hi, tree [sayHello]
    

    深入理解注解

    如果换一个角度理解注解:它的本质是一个继承了Annotation接口的接口

    当我们通过getAnnotation()方法获取一个注解的时候,JDK会通过动态代理生成注解的代理类$Proxy1,这个代理类代理了Tree中的所有方法,其实本质上还是通过反射来实现的,但是我们逐步递进的分析,先研究注解,下一步研究JDK动态代理,最后才能到达反射。

    JAVA 中专门用于处理注解的 Handler:

    sun.reflect.annotation.AnnotationInvocationHandler

    AnnotationInvocationHandler有如下几个属性:

    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;
    private transient volatile Method[] memberMethods = null;

    其中memberValues在初始化后,key是注解的属性值,value是我们为属性的赋值,可能你已经忘了我们在程序中是怎么做的了 : @Tree(name = "Someone")

    在这里插入图片描述

    所有动态代理类生成的方法,都会走这个invoke()方法。

    而这个invoke方法做的事情,概括起来就是:通过方法名获取属性值。

    public Object invoke(Object var1, Method var2, Object[] var3) {
            String var4 = var2.getName();
            Class[] var5 = var2.getParameterTypes();
            if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
                return this.equalsImpl(var3[0]);
            } else if (var5.length != 0) {
                throw new AssertionError("Too many parameters for an annotation method");
            } else {
                byte var7 = -1;
                switch(var4.hashCode()) {
                case -1776922004:
                    if (var4.equals("toString")) {
                        var7 = 0;
                    }
                    break;
                case 147696667:
                    if (var4.equals("hashCode")) {
                        var7 = 1;
                    }
                    break;
                case 1444986633:
                    if (var4.equals("annotationType")) {
                        var7 = 2;
                    }
                }
    
                switch(var7) {
                case 0:
                    return this.toStringImpl();
                case 1:
                    return this.hashCodeImpl();
                case 2:
                    return this.type;
                default:
                    Object var6 = this.memberValues.get(var4);
                    if (var6 == null) {
                        throw new IncompleteAnnotationException(this.type, var4);
                    } else if (var6 instanceof ExceptionProxy) {
                        throw ((ExceptionProxy)var6).generateException();
                    } else {
                        if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                            var6 = this.cloneArray(var6);
                        }
                        return var6;
                    }
                }
            }
        }
    

    从代码中我们可以看到,如果匹配为toString(),hashCode(),annotationType,会专门返回相应的实现,否则就返回memberValues对应key中相应的value,这样看起来还是比较清晰的。

    于是,在分析完注解的实现后,我们同时也有了下一步的研究目标:JDK动态代理的实现。

    servlet 3.0引入的新注解

    此篇文章的缘起,是由于在阅读SpringBoot源码时看到了@HandlesTypes注解,这是Tomcat的SCI机制用到的一个注解,被它标明的类需要作为参数值传入到onStartup方法。这是Servlet3.0新增的特性,所以在这里也列举一下Servlet3.0中新增的一些注解:

    HandlesTypes –该注解用来表示一组传递给ServletContainerInitializer的应用类。

    HttpConstraint – 该注解代表所有HTTP方法的应用请求的安全约束,和ServletSecurity注释中定义的HttpMethodConstraint安全约束不同。

    HttpMethodConstraint – 指明不同类型请求的安全约束,和ServletSecurity 注解中描述HTTP协议方法类型的注释不同。

    MultipartConfig –该注解标注在Servlet上面,表示该Servlet希望处理的请求的 MIME 类型是 multipart/form-data。

    ServletSecurity 该注解标注在Servlet继承类上面,强制该HTTP协议请求遵循安全约束。

    WebFilter – 该注解用来声明一个Server过滤器;

    WebInitParam – 该注解用来声明Servlet或是过滤器的中的初始化参数,通常配合 @WebServlet 或者 @WebFilter 使用。

    WebListener –该注解为Web应用程序上下文中不同类型的事件声明监听器。

    WebServlet –该注解用来声明一个Servlet的配置。

    JDK动态代理

    实验:实现基于JDK的动态代理

    【第一步】定义一个接口和实现类

    public interface IPerson {
        void sayHello();
    }
    
    @Service
    public class PersonServiceImpl implements IPerson {
    
        @Override
        public void sayHello() {
            System.out.println("Hello~~~");
        }
    }
    
    public class PersonInvocationHandler implements InvocationHandler {
    
        private Object target;
    
        public PersonInvocationHandler(Object target){
            this.target = target;
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------前置逻辑-------------");
            // 执行相应的目标方法
            Object result = method.invoke(target,args);
            System.out.println("------后置逻辑-------------");
            return result;
        }
    }
    

    【第二步】两种使用JDK动态代理的方式

    方式一:分为五个步骤

    1.实现InvocationHandler接口

    2.获得动态代理类:Proxy.getProxyClass

    3.获得代理类的构造方法:

    4.getConstructor(InvocationHandler.class)

    5.获得代理对象,传入自定义的InvocationHandler
    通过代理对象调用目标方法

    方式二 - 提供了一种封装好的方法:

    java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

    三个参数代表的含义分别是:

    ClassLoader loader:接口的类加载器

    Class<?>[] interfaces:接口(可以是一组接口)

    InvocationHandler h:自定义的InvocationHandler

    下面是两种方式的示例:

    public class DynamicProxyClient {
    
        public static void main(String[] args) throws Exception{
            // 方式一
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            Class proxyClazz = Proxy.getProxyClass(IPerson.class.getClassLoader(),IPerson.class);
            Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
            IPerson iPerson = (IPerson) constructor.newInstance(new PersonInvocationHandler(new PersonServiceImpl()));
             iPerson.sayHello();
    
            // 方式二
            IPerson  proxyInstance = (IPerson) Proxy.newProxyInstance(IPerson.class.getClassLoader(),  new Class[]{IPerson.class},  new PersonInvocationHandler(new PersonServiceImpl())); proxyInstance.sayHello();
    }
    }
    

    综上所述,基于JDK的动态代理的实现方式:实现InvocationHandler接口,重写invoke()方法。

    源码分析

    首先要说明一点,JDK动态代理只能代理接口,因为代理类本身已经继承了Proxy,而Java不允许多重继承,但是允许实现多个接口。

    我们先来看那个封装好的newProxyInstance()方法源码:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            // 生成代理类
            Class<?> cl = getProxyClass0(loader, intfs);
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
            // 获取代理类的构造方法,其中constructorParams是InvocationHandler.class
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                // 返回代理类
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    

    ProxyClassFactory 方法

     private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // 组合成为代理类名字
            private static final String proxyClassNamePrefix = "$Proxy";
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {
                  
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                    }
                    
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
               
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                // 生成代理类文件
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
                    // native方法,返回代理类
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                   
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }
    

    《深入理解Java虚拟机时》,第七章有这样一段话:

    在类加载的加载阶段,虚拟机完成三件事情:

    1.通过一个类的全限定名来获取定义此类的二进制字节流

    2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

    3.在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据访问入口

    由于虚拟机规范对这3点要求并不具体,所以实际的实现是非常灵活的,关于第1点,获取类的二进制字节流(class字节码)就有很多途径:

    从ZIP包获取,这是JAR、EAR、WAR等格式的基础

    从网络中获取,典型的应用是 Applet

    运行时计算生成,这种场景使用最多的是动态代理技术,在 java.lang.reflect.Proxy 类中,就是用了

    ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流
    由其它文件生成,典型应用是JSP,即由JSP文件生成对应的Class类
    从数据库中获取等等

    其中的“运行时计算生成 - ProxyGenerator.generateProxyClass”不就在这里吗~~

     public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
            ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
            final byte[] var4 = var3.generateClassFile();
            if (saveGeneratedFiles) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        try {
                            int var1 = var0.lastIndexOf(46);
                            Path var2;
                            if (var1 > 0) {
                                Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                                Files.createDirectories(var3);
                                var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                            } else {
                                var2 = Paths.get(var0 + ".class");
                            }
    
                            Files.write(var2, var4, new OpenOption[0]);
                            return null;
                        } catch (IOException var4x) {
                            throw new InternalError("I/O exception saving generated file: " + var4x);
                        }
                    }
                });
            }
    
            return var4;
        }
    

    如果继续深入研究,下面的这些方法,基本都是一些IO文件写入的操作,就不粘贴源码了。

    至此,基于JDK的动态代理的分析就到此为止,其本质上是基于反射机制,在运行时创建代理类。所以下一步的研究对象,理所当然的就是Java的反射机制了。

    Java反射

    简介

    允许运行时的Java程序获取自身信息,同时操作类或对象的内部属性,最通俗易懂的解释,就是让你根据一个String来得到你要的实体对象*

    功能:

    1. 运行时判断对象所属类
    2. 运行时构造类的对象
    3. 运行时判断类的属性和方法
    4. 运行时调用任意一个类的方法

    下面是相关的四个类,包含了我们定义一个类所能用到的所有属性和方法。

    1. Class
    2. Field
    3. Method
    4. Constructor

    常用方法

    获取Class对象
    1. Class类中的静态方法forName
    2. 直接获取某一个对象的 class
    3. 调用某个对象的 getClass() 方法
    反射获取类的属性

    getFields():Field[] - 只能获取public的字段,包括父类的
    getDeclaredFields():Field[]- 只能获取自己声明的各种字段,包括public,protected,private

    获取方法:

    getMethod(String,Class<?>…):Method- 根据方法名和参数获取方法
    getDeclaredMethods():Method[]

    invoke()

    调用包装在当前Method对象中的方法

    Method和NativeMethodAccessorImpl类中的invoke()方法:

    public Object invoke(Object obj, Object... args)
           throws IllegalAccessException, IllegalArgumentException,
              InvocationTargetException
       {
           if (!override) {
               if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                   Class<?> caller = Reflection.getCallerClass();
                   checkAccess(caller, clazz, obj, modifiers);
               }
           }
           MethodAccessor ma = methodAccessor;
           if (ma == null) {
               ma = acquireMethodAccessor();
           }
           return ma.invoke(obj, args);
       }
       
       
       public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
           if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
               MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
               this.parent.setDelegate(var3);
           }
           return invoke0(this.method, var1, var2);
       }
    

    在上述方法中,有两点是比较重要的:

    1. 根据Class对象获取Method方法时,参数:方法名和参数的CLass类型
    2. 调用method.invoke(obj, args)

    原理探究

    如果想要理解反射,就首先要对Java类加载的方式有了解。

    我们知道,我们写的Java文件会被编译成.class文件(字节码文件),才能在JVM中执行,是通过类加载器进行加载,

    java.lang.ClassLoader#loadClass(java.lang.String, boolean)是加载一个类的核心方法,

     protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
    
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    

    主要逻辑分为3步

    1. 检查是否已加载,如果已加载则直接返回
    2. 遵循双亲委派模式,先调用父类加载器加载器
    3. 如果父类加载器加载失败,调用findClass加载

    当我们打开findClass方法时,会不会有些诧异,直接抛了个异常,这是因为ClassLoader是一个抽象类,是无法通过new创建一个对象的,也就是说,你无法通过ClassLoader类直接加载.class文件,只能写一个子类继承ClassLoader,然后覆盖findClass方法,定义自己的加载逻辑。

     protected Class<?> findClass(String name) throws ClassNotFoundException {
            throw new ClassNotFoundException(name);
        }
    

    在自定义加载逻辑的时候,通常的做法是,读取一个字节数组byte[] b,然后调用ClassLoader的defineClass()方法,于是返回了一个Class对象

     private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                             ProtectionDomain pd);
    
        private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                             ProtectionDomain pd, String source);
    
        private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                             int off, int len, ProtectionDomain pd,
                                             String source);
    

    Class类的内部类 ReflectionData,里面的内容与xxx.class文件中的内容映射,作用就是缓存从JVM中读取类的如下属性数据。同时由于一个类中包含的内容信息量过大,所以被拆成了四个类,也就是上面提到的- Class、Field、Method、Constructor

     private static class ReflectionData<T> {
           volatile Field[] declaredFields;
           volatile Field[] publicFields;
           volatile Method[] declaredMethods;
           volatile Method[] publicMethods;
           volatile Constructor<T>[] declaredConstructors;
           volatile Constructor<T>[] publicConstructors;
           // Intermediate results for getFields and getMethods
           volatile Field[] declaredPublicFields;
           volatile Method[] declaredPublicMethods;
           volatile Class<?>[] interfaces;
    
           // Value of classRedefinedCount when we created this ReflectionData instance
           final int redefinedCount;
    
           ReflectionData(int redefinedCount) {
               this.redefinedCount = redefinedCount;
           }
       }
    
    展开全文
  • 本文所介绍的Java底层技术,有着逐渐递进的特点,Java注解中使用了JDK动态代理,而JDK动态代理中运用了Java反射。Java注解当我们阅读框架源码时,会看到其中包含着大量的注解,注解被广泛使用的原因在于,可以...
  • Java 三大框架常用的技术_Spring部分

    千次阅读 2013-01-18 09:33:46
    三大框架常用的技术 Spring技术:(我懂的我才写,我也不知道对不对,写在这里主要是我的个人笔记。不对别骂人,呵呵) 1.  Spring mvc: 很多企业只使用Spring+Hibernate框架,不使用Struts2+hiber+Spring。 ...
  • Java三大主流框架概述

    2019-10-01 18:29:11
    Java三大主流框架概述 邮箱:1727292697@qq.com Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的?我们知道...
  • 集合框架Java集合框架Java语言的重要组成部分它包含了系统而完整的集合层次体系封装了大量的数据结构的实现深刻理解Java集合框架的组成结构及其中的实现类和算法能极提高程序员编码的能力本章讲述Java集合框架...
  • 而掌握Java框架,不管在成熟的公司,快速发展的公司,还是创业阶段的公司,都能对当前正在开发中的系统有整体的认知,从而更好的熟悉和学习技术,这篇文章胖达就打算给大家介绍几款现阶段流行的框架组合,不管是在...
  • Java三大主流框架

    千次阅读 2018-07-29 12:51:33
    Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的? 我们知道,传统的Java Web应用程序是采用JSP+Servlet+...
  • java三大主流框架

    2019-10-08 21:38:02
    Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的?我们知道,传统的JavaWeb应用程序是采用JSP+Servlet+...
  • 本文所介绍的Java底层技术,有着逐渐递进的特点,Java注解中使用了JDK动态代理,而JDK动态代理中运用了Java反射。Java注解当我们阅读框架源码时,会看到其中包含着大量的注解,注解被广泛使用的原因在于,可以...
  • java三大框架

    2018-04-12 14:11:10
    Java开发中,我们经常使用Struts、Hibernate和Spring个主流框架,但你是否知道这框架最初是为解决怎样的问题而生的? Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给...
  • Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的? 我们知道,传统的Java Web应用程序是采用JSP+Servlet+...
  • Java三大主流框架介绍

    2020-02-07 17:31:08
    Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。 传统的Java Web应用程序是采用JSP+Servlet+Javabean来实现的,这种模式实现了最基本的MVC分层,使的...
  • java三大主流框架概述

    2016-12-29 20:46:37
    Struts、Hibernate和Spring是我们Java开发中的常用框架,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的? 我们知道,传统的Java Web应用程序是采用JSP+Servlet+...
  • JAVA三大主流框架概述

    2017-07-25 20:43:22
    Struts、Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案。但你是否知道,这些知名框架最初是怎样产生的?我们知道,传统的Java Web应用程序是采用JSP+Servlet+...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 753
精华内容 301
关键字:

常用三大java框架

java 订阅