`
Kingson_Wu
  • 浏览: 123694 次
文章分类
社区版块
存档分类
最新评论

struts2+hibernate+spring整合(annotation版)

 
阅读更多

本文使用struts2,hibernate,spring技术整合Web项目,同时分层封装代码,包含model层,DAO层,Service层,Action层。

在整合hibernate时使用annotation注释进行数据库的映射,整合spring时使用annotation进行IOC注入。

最后在DAO层中继承HibernateDaoSupport来编写代码。



首先看一下项目的目录:




需要的类库:









以person为例:


model层:

package com.hwadee.tradeUnion.model;


import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;


/**
 * Person entity. @author MyEclipse Persistence Tools
 */
@Entity
public class Person implements java.io.Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	// Fields

	private int id;
	private String name;
	private String sex;
	private String nationality;
	private String area;
	private Date birthday;
	private String education;
	private String polity;
	private String company;
	private String idCard;
	private String phone;

	//------------------------------------------
	private Set<PersonApply> personApplys = new HashSet<PersonApply>();
	private Set<Honour> Honours = new HashSet<Honour>();
	private Death death;	
	
	
	// Property accessors
	@Id
	@GeneratedValue
	public int getId() {
		return this.id;
	}
	
	public void setId(int id) {
		this.id = id;
	}


	//-------------------------------------------------
	@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.ALL})
	@JoinColumn(name="personId")
	public Set<PersonApply> getPersonApplys() {
		return personApplys;
	}

	public void setPersonApplys(Set<PersonApply> personApplys) {
		this.personApplys = personApplys;
	}

	
	
	
	@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.ALL})
	@JoinColumn(name="personId")
	public Set<Honour> getHonours() {
		return Honours;
	}

	public void setHonours(Set<Honour> honours) {
		Honours = honours;
	}

	@OneToOne
	@JoinColumn(name="deathId")
	public Death getDeath() {
		return death;
	}

	public void setDeath(Death death) {
		this.death = death;
	}
	
	
	//------------------------------------------------------

	@Column(length = 30)
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Column(length = 10)
	public String getSex() {
		return this.sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@Column( length = 10)
	public String getNationality() {
		return this.nationality;
	}

	public void setNationality(String nationality) {
		this.nationality = nationality;
	}

	@Column( length = 50)
	public String getArea() {
		return this.area;
	}

	public void setArea(String area) {
		this.area = area;
	}

	public Date getBirthday() {
		return this.birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	@Column( length = 10)
	public String getEducation() {
		return this.education;
	}

	public void setEducation(String education) {
		this.education = education;
	}

	@Column(length = 10)
	public String getPolity() {
		return this.polity;
	}

	public void setPolity(String polity) {
		this.polity = polity;
	}

	@Column( length = 30)
	public String getCompany() {
		return this.company;
	}

	public void setCompany(String company) {
		this.company = company;
	}

	@Column(length = 18)
	public String getIdCard() {
		return this.idCard;
	}

	public void setIdCard(String idCard) {
		this.idCard = idCard;
	}

	@Column(length = 11)
	public String getPhone() {
		return this.phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

}

DAO层:


package com.hwadee.tradeUnion.dao;

import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.hwadee.tradeUnion.model.Person;


@Component
public class PersonDAO extends HibernateDaoSupport {
	private static final Logger log = LoggerFactory.getLogger(PersonDAO.class);
	// property constants
	public static final String NAME = "name";
	public static final String SEX = "sex";
	public static final String NATIONALITY = "nationality";
	public static final String AREA = "area";
	public static final String EDUCATION = "education";
	public static final String POLITY = "polity";
	public static final String COMPANY = "company";
	public static final String ID_CARD = "idCard";
	public static final String PHONE = "phone";

	protected void initDao() {
		// do nothing
	}
	
	//--------注入spring配置的sessionFactory
	@Autowired  
	public void setMySessionFactory(SessionFactory sessionFactory){  
	    super.setSessionFactory(sessionFactory);  
	}  
	

	public void save(Person transientInstance) {
		log.debug("saving Person instance");
		try {
			getHibernateTemplate().save(transientInstance);
			log.debug("save successful");
		} catch (RuntimeException re) {
			log.error("save failed", re);
			throw re;
		}
	}

	public void delete(Person persistentInstance) {
		log.debug("deleting Person instance");
		try {
			getHibernateTemplate().delete(persistentInstance);
			log.debug("delete successful");
		} catch (RuntimeException re) {
			log.error("delete failed", re);
			throw re;
		}
	}
	
	
	public void deleteById(int id){
		
		
		Person temp=this.findById(id);
		this.delete(temp);
		
		
	}
	
	
	public void update(Person temp){
		
		getHibernateTemplate().update(temp);
		
		
	}
	

	public Person findById(int id) {
		log.debug("getting Person instance with id: " + id);
		try {
			Person instance = (Person) getHibernateTemplate().get(
					Person.class, id);
			return instance;
		} catch (RuntimeException re) {
			log.error("get failed", re);
			throw re;
		}
	}

	public List findByExample(Person instance) {
		log.debug("finding Person instance by example");
		try {
			List results = getHibernateTemplate().findByExample(instance);
			log.debug("find by example successful, result size: "
					+ results.size());
			return results;
		} catch (RuntimeException re) {
			log.error("find by example failed", re);
			throw re;
		}
	}

	public List findByProperty(String propertyName, Object value) {
		log.debug("finding Person instance with property: " + propertyName
				+ ", value: " + value);
		try {
			String queryString = "from Person as model where model."
					+ propertyName + "= ?";
			return getHibernateTemplate().find(queryString, value);
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}

	public List findByName(Object name) {
		return findByProperty(NAME, name);
	}

	public List findBySex(Object sex) {
		return findByProperty(SEX, sex);
	}

	public List findByNationality(Object nationality) {
		return findByProperty(NATIONALITY, nationality);
	}

	public List findByArea(Object area) {
		return findByProperty(AREA, area);
	}

	public List findByEducation(Object education) {
		return findByProperty(EDUCATION, education);
	}

	public List findByPolity(Object polity) {
		return findByProperty(POLITY, polity);
	}

	public List findByCompany(Object company) {
		return findByProperty(COMPANY, company);
	}

	public List findByIdCard(Object idCard) {
		return findByProperty(ID_CARD, idCard);
	}

	public List findByPhone(Object phone) {
		return findByProperty(PHONE, phone);
	}

	public List findAll() {
		log.debug("finding all Person instances");
		try {
			String queryString = "from Person";
			return getHibernateTemplate().find(queryString);
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}

	public Person merge(Person detachedInstance) {
		log.debug("merging Person instance");
		try {
			Person result = (Person) getHibernateTemplate().merge(
					detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	public void attachDirty(Person instance) {
		log.debug("attaching dirty Person instance");
		try {
			getHibernateTemplate().saveOrUpdate(instance);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void attachClean(Person instance) {
		log.debug("attaching clean Person instance");
		try {
			getHibernateTemplate().lock(instance, LockMode.NONE);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public static PersonDAO getFromApplicationContext(ApplicationContext ctx) {
		return (PersonDAO) ctx.getBean("PersonDAO");
	}
	//输入Hql查询函数
	public List findByHql( String hql) {
		log.debug("finding  Admin By Hql");
		try {
			String queryString = hql;
			return getHibernateTemplate().find(queryString);
		} catch (RuntimeException re) {
			log.error("find all failed", re);
			throw re;
		}
	}
}


Service层:


package com.hwadee.tradeUnion.service;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.hwadee.tradeUnion.dao.PersonDAO;

import com.hwadee.tradeUnion.model.Person;

@Component
public class PersonService {

	private PersonDAO personDAO;


	public PersonDAO getPersonDAO() {
		return personDAO;
	}

	@Resource
	public void setPersonDAO(PersonDAO personDAO) {
		this.personDAO = personDAO;
	}

	
	public void add(Person kxw) {
	

		personDAO.save(kxw);
		
	}

	public void delete(Person kxw) {
	
		personDAO.delete(kxw);
	}

	public void deleteById(int id) {

		personDAO.deleteById(id);
	}

	public List<Person> list() {
	
	
		return personDAO.findAll();
		
		
	}

	public Person loadById(int id) {
		
		return personDAO.findById(id);
	}

	public void update(Person kxw) {
		
		personDAO.update(kxw);
	
	
}

Action层:

package com.hwadee.tradeUnion.action;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.hwadee.tradeUnion.model.Person;
import com.hwadee.tradeUnion.service.PersonService;
import com.opensymphony.xwork2.ActionSupport;


@Component("PersonAction")
public class PersonAction extends ActionSupport {
		
			/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

			private List<Person> personList;
		
			private PersonService personService; 
			private Person person;
			private int id;

			public PersonService getPersonService() {
				return personService;
			}


			@Resource
			public void setPersonService(PersonService personService) {
				this.personService = personService;
			}



			public String list() {
				personList = personService.list();

				return SUCCESS;
			}
			
			
			public String add() {
				personService.add(person);
		
				return SUCCESS;
			}
			public String update() {
				personService.update(person);
				return SUCCESS;
			}
			public String delete() {
					
				personService.deleteById(id);
				return SUCCESS;
			}
			public String addInput() {
				
				return INPUT;
			}
			public String updateInput() {
				this.person = this.personService.loadById(id);
				return INPUT;
			}
			
			public String load(){
				
				this.person=this.personService.loadById(id);
				
				return SUCCESS;
			}

			public List<Person> getPersonList() {
				return personList;
			}


			public void setPersonList(List<Person> personList) {
				this.personList = personList;
			}


			public Person getPerson() {
				return person;
			}


			public void setPerson(Person person) {
				this.person = person;
			}


			public int getId() {
				return id;
			}


			public void setId(int id) {
				this.id = id;
			}
		
	}


	

applicationContext.xml文件:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
           
	
<context:annotation-config />
<context:component-scan base-package="com.hwadee" />
	<!-- 
		<bean id="dataSource"
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		
		
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/spring" />
		<property name="username" value="root" />
		<property name="password" value="bjsxt" />
		</bean>
	-->

	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>classpath:jdbc.properties</value>
		</property>
	</bean>

	<bean id="dataSource" destroy-method="close"
		class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
	
	

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<!-- 
		<property name="annotatedClasses">
			<list>
				<value>com.bjsxt.model.User</value>
				<value>com.bjsxt.model.Log</value>
			</list>
		</property>
		 -->
		 <property name="packagesToScan">
			<list>
				<value>com.hwadee.tradeUnion.model</value>
				
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
				<prop key="hibernate.format_sql">true</prop>
			</props>
		</property>
	</bean>
	
	
	<!--  <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>-->

	<bean id="txManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<aop:config>
		<aop:pointcut id="bussinessService"
			expression="execution(public * com.hwadee.tradeUnion.service.*.*(..))" />
		<aop:advisor pointcut-ref="bussinessService"
			advice-ref="txAdvice" />
	</aop:config>

	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<!--<tx:method name="exists" read-only="true" />  -->
			<tx:method name="list" read-only="true" /> 
			<tx:method name="add*" propagation="REQUIRED"/>
			<tx:method name="loadById*" read-only="true"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="deleteById*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>

</beans>

web.xml文件:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Struts Blank</display-name>


    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    
     <!--如果不定义webAppRootKey参数,那么webAppRootKey就是缺省的"webapp.root"-->   

   <!--   <context-param>

        <param-name>webAppRootKey</param-name>

        <param-value>ssh.root</param-value>

    </context-param>-->

    <context-param>

        <param-name>log4jConfigLocation</param-name>

        <param-value>classpath:log4j.properties</param-value>

    </context-param>

    <context-param>

        <param-name>log4jRefreshInterval</param-name>

        <param-value>60000</param-value>

    </context-param>
<!--配置log4j -->



    <listener>

        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

    </listener>
    
    
    <listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
		<!-- default: /WEB-INF/applicationContext.xml -->
	</listener>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<!-- <param-value>/WEB-INF/applicationContext-*.xml,classpath*:applicationContext-*.xml</param-value>  -->
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

		
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>GBK</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<filter>
		<filter-name>openSessionInView</filter-name>
		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
		<init-param>
			<param-name>sessionFactoryBeanName</param-name>
			<param-value>sessionFactory</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>openSessionInView</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
    
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

jdbc.properties:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3300/TradeUnion
jdbc.username=root
jdbc.password=123456


struts.xml:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">


<struts>
 
    <constant name="struts.devMode" value="true" />


      <package name="default" namespace="/" extends="struts-default">

        <action name="hello">
            <result>
                /hello.jsp
            </result>
        </action>
        
            <action name="*Login" class="{1}LoginAction">
          <result name="success">/{1}/{1}-Manage.jsp</result>
      <result name="fail">fail.jsp</result>
        </action>
           
        
  <action name="*-*-*" class="{2}Action" method="{3}">
          <result name="success">/{1}/{2}-{3}.jsp</result>
            <result name="input">/{1}/{2}-{3}.jsp</result>
          <result name="fail">/{1}/{2}-{3}-fail.jsp</result>
      
        </action>
        
    </package>
    
    <!-- Add packages here -->


</struts>






































































分享到:
评论

相关推荐

    Struts1.3 + Hibernate3.3 + Spring3.0 Annotation整合

    Struts1.3、Hibernate3.3和Spring3.0是经典的Java企业级开发框架,它们的整合在早期Web应用开发中非常常见。这三种框架的结合提供了模型-视图-控制器(MVC)架构、对象关系映射(ORM)以及依赖注入(DI)和面向切面...

    struts+spring+hibernate三大框架整合

    **Spring整合Struts** Spring整合Struts主要有三种方式: 1. **使用Spring的ActionSupport**:Action类直接继承自Spring的ActionSupport,通过`super.getWebApplicationContext()`获取Spring上下文,然后通过`...

    struts2+spring2+hibernate3 Annotation的整合

    本文将详细解析"Struts2+Spring2+Hibernate3 Annotation的整合"这一主题,主要涵盖以下几个方面: 1. **Struts2框架**:Struts2是一个基于MVC设计模式的Web应用框架,用于简化Java Web应用程序的开发。它的主要功能...

    Struts2+Spring2.5+Hibernate3+Freemarker框架整合

    整合S2SH+Freemarker+oscache,后台用Spring管理各个bean,Hibernate做数据库持久化,viewer用Freemarker。整合中对Struts2,Hibernate,Spring都采用Annotation进行注解类。

    Struts2+Spring2.5+Hibernate3+Freemarker整合

    整合S2SH+Freemarker,后台用Spring管理各个bean,Hibernate做数据库持久化,viewer用Freemarker。整合中对Struts2,Hibernate,Spring都采用Annotation进行注解类。

    Struts2+Hibernate4+Spring3整合(注解和XML方式都有)

    在"Struts2+Hibernate4+Spring3整合"中,通常有两种方式实现整合: 1. **XML方式**:这是传统的配置方式,所有的配置信息都在XML文件中。Struts2的配置文件(struts.xml)定义Action和结果,Hibernate的配置文件...

    基于Annotation的Struts2.0+Hibernate3.3+Spring2.5图文教程整合开发.doc

    【基于Annotation的Struts2.0+Hibernate3.3+Spring2.5整合开发】 这篇文档主要介绍了一个使用注解(Annotation)进行Struts2.0、Hibernate3.3和Spring2.5整合开发的教程。这种集成方式相比传统的XML配置,可以简化...

    基于Annotation的Struts2.0+Hibernate3.3+Spring2.5整合开发

    Struts2.0、Hibernate3.3和Spring2.5是经典的Java企业级开发框架,它们各自负责不同的职责:Struts2处理MVC模式中的控制层,Hibernate负责对象关系映射,Spring则提供了全面的依赖注入(DI)和面向切面编程(AOP)...

    Struts2+Spring2.5+Hibernate3+annotation 整合程序

    这个"Struts2+Spring2.5+Hibernate3+annotation 整合程序"旨在展示如何将这三大框架与注解(Annotation)技术结合,以实现更高效、更简洁的代码编写。 Struts2是一个基于MVC设计模式的Web应用框架,它主要负责处理...

    基于注解Annotation的最新版SSH(Struts2.3.7+Hibernate4.1.9+Spring3.2.0)框架整合开发

    基于注解Annotation的最新版SSH(Struts2.3.7+Hibernate4.1.9+Spring3.2.0)整合开发,真正实现零配置。 最新版本Struts、Spring、Hibernate框架整合: struts-2.3.7 spring-framework-3.2.0.RELEASE hibernate-...

    struts2+hibernate+annotation+spring整合的jar包

    Struts2、Hibernate和Spring是Java开发中三大主流框架,它们的整合应用通常被称为SSH(Struts2-Spring-Hibernate)集成。这个压缩包包含了这三个框架的特定版本:Struts2.18、Hibernate3.3.2和Spring2.5.6,以及可能...

    Struts2.3.28+spring4.1.6+hibernate4.3.8整合总结(注解)

    Struts2.3.28、Spring4.1.6和Hibernate4.3.8是三个经典的Java EE框架,它们的整合是企业级应用开发中常见的技术栈,通常被称为S2SH。在这个版本的整合中,注解的使用极大地简化了配置过程,使得开发更加高效。以下是...

    struts2.0+hibernate3.0+spring2.0整合(2)

    Struts2.0、Hibernate3.0和Spring2.0是Java Web开发中经典的三大框架,它们的整合是企业级应用开发的常见实践。Struts2作为MVC(Model-View-Controller)框架,主要负责处理HTTP请求并调度业务逻辑;Hibernate是一个...

    基于Annotation的Struts2.0+Hibernate3.3+Spring2.5整合开发 (1)

    标题中的“基于Annotation的Struts2.0+Hibernate3.3+Spring2.5整合开发”指的是使用注解的方式将三个流行的Java企业级框架——Struts2、Hibernate和Spring进行集成开发。这样的集成有助于简化配置,提高代码的可读性...

    struts2+spring2+hibernate3 Annotation整合

    Struts2、Spring2和Hibernate3是Java Web开发中的三个重要框架,它们分别负责MVC模式中的Action层、业务逻辑层和服务数据访问层。将这三个框架整合在一起,可以实现高效、松耦合的Web应用程序。这里我们将深入探讨...

Global site tag (gtag.js) - Google Analytics